Java核心技术-集合-映射
一 基本映射操作
1 映射用来存放键/值对
2 映射提供了两种通用的实现:HashMap和TreeMap,两个类都实现了Map接口
散列映射对键进行散列;树映射用键的整体顺序对元素进行排序,并将其组织成搜索树
散列或比较函数只能作用于键,与键关联的值不能进行散列或比较
3应该如何选择散列或者树?与集合一样,散列的速度稍快一些,如果不需要按照排列顺序访问键,就最好选择散列。
4
Map<String,Employee> staff = new HashMap<>();//HashMap implements Map
Employee harry = new Employee("tiffany");
staff.put("3245", harry);
在这里键是一个字符串,对应的值是Employee对象。要想检索一个对象,必须使用一个键。
String id = "3245";
e = staff.get(id)
如果在映射中没有与给定键对应的信息,get就返回null
5 键必须是唯一的,不能对同一个键存放两个值。如果对同一个键两次调用put方法,第二个值就会取代第一个值。
6 用forEach迭代处理映射的键和值,例
staff.forEach((k,v)->
System.out.println("key"+k+",value="+v));
Map类常用方法:
V get(Object key):获取与键对应的值或对象
default V getOrDefault(Object key,V defaultValue):获取与键关联的值或对象
V put(K key,V value):将键与对应得到值关系插入到映射中
。。。
二 更新映射项
1 如何处理更新映射项时键第一次出现的情况:
-
counts.put(word,counts.getOrDefault(word,0)+1); counts,putIfAbsent(word,0); counts.put(word,counts.get(word)+1); counts.merge(word,1,Integer::sum);//将把word与1关联,否则使用Integer::sum函数组合原值和1
-
三 映射视图
1 三种视图:键集,值集合,键/值对集
-
Set<K> keySet() Collection<V> values() Set<Map.Entry<K,V>> entrySet()
这三种方法会分别返回这3个视图,其中keySet既不是HashSet也不是TreeSet,而是实现了Set接口的另外某个类的对象。Set接口扩展了Collection接口,因此可以像使用集合一样使用keySet
例1:
Set<String> keys = map.keySet();
for(String key:keys){
//可以枚举一个映射的所有键
}
例2:同时查看键和值:
for(Map.Entry<String,Employee> entry:staff.entry.entrySet()){
String k = entry.getKey();
Employee v = entry.getValue();
}
四 弱散列映射
1 垃圾回收器跟踪活动的对象,只要映射对象是活动的,其中的所有桶也是活动的,它们不能被回收。所以需要由程序负责从长期存活的映射表中删除那些无用的值,或者使用WeakHashMap类
2 WeakHashMap使用弱引用来保存键,WeakHashMap对象将引用保存到另外一个对象(散列键)中
3 通常,如果垃圾回收器发现某个特定的对象已经没有他人引用了,就将其回收。但对于只能由WeakReference引用的对象,垃圾回收器仍然要回收它,但是要将引用这个对象的弱引用放入队列中。WeakHashMap将周期性地检查队列,以便找出新添加的弱引用,然后将其删除
五 链接散列集和映射
1 为了避免在散列表中的项从表面上看起来是随机排列的,可以使用LinkedHashSet和LinkedHashMap类来记住插入元素项的顺序。
例:
Map<String,Employee> staff = new LinkedHashMap<>();
staff.put("1234", new Employee("tiffany"));
staff.put("2345", new Employee("sarah"));
staff.put("1345", new Employee("jeff"));
staff.put("3345", new Employee("jelly"));
Set<String> keys = staff.keySet();
for (String key : keys) {
System.out.println(key);
}
for (Employee e : staff.values()) {
System.out.println(e.getName());
}
2 链接散列映射将用访问顺序,而不是插入顺序,对映射条目进行迭代。
3 可以使用LinkedHashMap来实现访问顺序这一目的:将访问频率高的元素放到内存中,而访问频率低的元素从数据库中读取
可以构造一个LinkedHashMap的子类,然后覆盖下面的方法
protected boolean removeEldestEntry(Map.Entry<K,V> eldest)
六 枚举集与映射
1.EnumSet是一个枚举类型元素集的高效实现。
2.EnumMap是一个键类型为枚举类型的映射。
七 标识散列映射(IdentityHashMap类)
在这个类中,键的散列值不是用hashCode 函数实现计算的,而是用System.identityHashCode方法计算的
在进行对象的比较时,使用的是== ,而不是equals,故比较的是对象
补充:==和equals的区别:
==比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作
equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。
例题:
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}