深入研究容器队列
1.Map
Map
用于保存具有映射关系的数据,因此Map
集合中保存着两组值,一组值用于保存Map
里的key
,另外一组值用于保存Map
里的value
,key
和value
都可以是任何引用类型的数据。Map的key
不允许重复,即同一个Map对象的任何两个key
通过equals()
方法比较总是返回false
。
如果把Map
里的所有key放在一起来看,它们就组成了一个Set
集合(所有的key
没有顺序,key
和key
之间不能重复),实际上,Map
确实包含了一个keySet()
方法,用于返回Map
里所有的key
组成的Set
集合。
如果把Map
里的所有value
放在一起看,它们又非常类似于一个List
:元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map
中的索引不再使用整数值,而是以另一个对象作为索引。如果需要从Map
中取出元素,需要提供该元素的key
索引。因此,Map
有时也被称为字典,或关联数组。
Map
接口中定义的常用方法有:
这里要注意的是V put(K key, V value)
方法,该方法用于给Map
中添加新的键值对,如果放入重复的key
时,新的value
会覆盖原有的value
;如果新的value
覆盖了原有的value
,该方法会返回被覆盖的value
。当然,如果没有key
重复,也就没有覆盖,该方法返回null
。
测试如下:
public static void main(String[] args) {
HashMap<String, String> hm = new HashMap<>();
hm.put("a", "123");
System.out.println(hm.put("b", "456"));
System.out.println(hm.put("a", "789"));
}
输出如下:
null
123
第一次输出,并没有覆盖,所以方法返回null
;第二次输出,由于Map
中已经有了a
这个键,所以原有的123
被覆盖并返回。
2.HashMap与Hashtable
HashMap
和Hashtable
都是Map
接口的典型实现类,它们之间的关系完全类似于ArrayList
和Vector
的关系:Hashtable
是一个古老的Map
实现类,它从JDK1.0起就出现了,当它出现时,Java还没有提供Map
接口。
HashMap
与Hashtable
存在两点典型区别:
- 1.
Hashtable
是一个线程安全的Map
实现,但HashMap
是线程不安全的实现,所以HashMap
比Hashtable
的性能高一点;但如果有多个线程访问同一个Map
对象时,使用Hashtable
实现类会更好。 - 2.
Hashtable
不允许使用null
作为key
和value
,但HashMap
可以。
为了成功地在HashMap
、Hashtable
中存储、获取对象,用作key
的对象必须实现hashCode()
方法和equals()
方法。
与HashSet
集合不能保证元素顺序一样,HashMap
和Hashtable
也不能保证其中的key-value对的顺序。类似于HashSet
,HashMap
和Hashtable
判断两个key
相等的标准也是:两个key
的hashCode
值相等,两个key
通过equals()
方法比较返回true
。
除此之外,HashMap
和Hashtable
中还包含一个containsValue()
方法,用于判断是否包含指定的value
。那么HashMap
和Hashtable
判断两个value
相等的标准是什么呢?这个标准更简单一点:只要两个对象通过equals()
方法比较返回true
即可。
我们可以做如下测试:
class A {
private int count;
public A(int count){
this.count = count;
}
// 根据count的值来判断两个对象是否相等
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj == null){
return false;
}
if(this.getClass() != obj.getClass()){
return false;
}
A a = (A)obj;
return this.count == a.count;
}
// 根据count来计算hashCode值
public int hashCode(){
return this.count;
}
}
class B{
@Override
public boolean equals(Object obj) {
return true;
}
}
public class HashtableTest {
public static void main(String[] args) {
Hashtable ht = new Hashtable<>();
ht.put(new A(1000),"Java");
ht.put(new A(2000),"Python");
ht.put(new A(3000),new B());
//由于Hashtable中有一个B对象,它与任何对象通过equals()方法比较都返回true
// 所以这里不管测试的字符串是什么,它都会返回true
System.out.println(ht.containsValue("123"));
// 对于key来说,如果两个A对象的hashCode值相等,并且通过equals()方法比较返回true
// 那么它们就是相等的,就是相同的key
System.out.println(ht.containsKey(new A(1000)));
}
}
程序输出:
true
true
1.因为
HashMap
、Hashtable
保存key
的方式与HashSet
保存集合元素的方式完全相同,所以HashMap
、Hashtable
对key
的要求与HashSet
对集合元素的要求完全相同。
2.也就是说,当使用自定义类作为HashMap
、Hashtable
的key
时,如果重写该类的equals(Object obj)
和hashCode()
方法,则应该保证两个方法的判断标准一致:当两个key
通过equals()
方法比较返回true
时,两个key
的hashCode()
返回值也应该相同。
3.LinkedHashMap
HashSet
有一个LinkedHashSet
子类,HashMap
也有一个LinkedHashMap
子类。LinkedHashMap
使用双向链表来维护key-value对的次序(其实只需要考虑key
的次序),该链表负责维护Map
的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。
LinkedHashMap
需要维护元素的插入顺序,因此性能略低于HashMap
的性能;但因为它以链表来维护内部顺序,所以在使用迭代器访问全部元素时有比较好的性能。
public class LinkedHashMapTest {
public static void main(String[] args) {
LinkedHashMap<String, Integer> scores = new LinkedHashMap<>();
scores.put("语文", 90);
scores.put("数学", 100);
scores.put("英语", 95);
// 调用forEach()方法遍历scores里所有的key-value对
scores.forEach((key,value) -> System.out.println(key + "-->" + value));
}
}
程序输出:
语文-->90
数学-->100
英语-->95
4.SortedMap接口和TreeMap实现类
正如Set
接口派生出SortedSet
子接口,SortedSet
接口有一个TreeSet
实现类一样,Map
接口也派生出一个SortedMap
子接口,SortedMap
接口也有一个TreeMap
实现类。
TreeMap
就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap
存储key-value对时,需要根据key
对节点进行排序。TreeMap
可以保证所有的key-value对处于有序状态。TreeMap
也有两种排序方式:
- 1.自然排序:
TreeMap
的所有key
必须实现Comparable
接口,而且所有的key
应该是同一个类的对象,否则将会抛出ClassCastException
异常。 - 2.定制排序:创建
TreeMap
时,传入一个Comparator
对象,该对象负责对TreeMap
中的所有key
进行排序。采用定制排序不需要Map
的key
实现Comparable
接口。
类似于TreeSet
中判断两个元素相等的标准,TreeMap
中判断两个key
相等的标准是:两个key
通过compareTo()
方法返回0。
与TreeSet
类似的是,TreeMap
中也提供了一系列根据key
顺序访问key-value对的方法:
方法 | 说明 |
---|---|
Map.Entry<K,V> firstEntry() |
返回一个与此映射中的最小键关联的键-值映射关系;如果映射为空,则返回 null |
K lastKey() |
返回此映射中当前第一个(最低)键,如果映射为空,则返回 null |
Map.Entry<K,V> lastEntry() |
返回与此映射中的最大键关联的键-值映射关系;如果映射为空,则返回 null |
K lastKey() |
返回此映射中当前最后一个(最高)键,如果映射为空,则返回 null |
Map.Entry<K,V> higherEntry(K key) |
返回一个键-值映射关系,它与严格大于给定键的最小键关联;如果不存在这样的键,则返回 null |
K higherKey(K key) |
返回严格大于给定键的最小键;如果不存在这样的键,则返回 null |
Map.Entry<K,V> lowerEntry(K key) |
返回一个键-值映射关系,它与严格小于给定键的最大键关联;如果不存在这样的键,则返回 null |
K lowerKey(K key) |
返回严格小于给定键的最大键;如果不存在这样的键,则返回 null |
NavigableMap<K,V> subMap(K fromKey,boolean fromInclusive,K toKey,boolean toInclusive) |
返回该Map的子Map,其key范围是从fromKey(是否包括取决于第二个参数)到toKey(是否包括取决于第四个参数) |
SortedMap<K,V> subMap(K fromKey,K toKey) |
返回此Map的子Map,其key范围是从fromKey(包括)到toKey(不包括) |
SortedMap<K,V> tailMap(K fromKey) |
返回该Map的子Map,其key的范围是大于fromKey(包括)的所有key |
NavigableMap<K,V> tailMap(K fromKey,boolean inclusive) |
返回该Map的子Map,其key的范围是大于fromKey(是否包括取决于第二个参数)的所有key |
SortedMap<K,V> headMap(K toKey) |
返回该Map的子Map,其key的范围是小于toKey(不包括)的所有key |
NavigableMap<K,V> headMap(K toKey,boolean inclusive) |
返回该Map的子Map,其key的范围是小于toKey(是否包括取决于第二个参数)的所有key |
针对这些方法做测试如下:
class R implements Comparable{
int count;
public R(int count) {
this.count = count;
}
//根据count值判断两个对象是否相等
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null || this.getClass() != obj.getClass()) return false;
R r = (R)obj;
return this.count == r.count;
}
// 根据count属性值来判断两个对象的大小
@Override
public int compareTo(Object o) {
R r = (R)o;
return this.count > r.count ? 1: this.count < r.count ? -1 : 0;
}
@Override
public String toString() {
return "R [count=" + count + "]";
}
}
public class TreeMapTest {
public static void main(String[] args) {
TreeMap tm = new TreeMap<>();
tm.put(new R(3),"Java");
tm.put(new R(-5),"Python");
tm.put(new R(9),"C++");
System.out.println(tm);
// 返回第一个键值对
System.out.println(tm.firstEntry());
// 返回最后一个键
System.out.println(tm.lastKey());
// 返回比new R(2)大的最小的key-value对
System.out.println(tm.higherEntry(new R(2)));
// 返回比new R(2)小的最大key值
System.out.println(tm.lowerKey(new R(2)));
// 返回key在new R(-1)和new R(4)之间的子Map
System.out.println(tm.subMap(new R(-1), new R(4)));
}
}
程序输出:
{R [count=-5]=Python, R [count=3]=Java, R [count=9]=C++}
R [count=-5]=Python
R [count=9]
R [count=3]=Java
R [count=-5]
{R [count=3]=Java}
- 点赞
- 收藏
- 关注作者
评论(0)