Java(7-3)

Part1
7-2介绍的是集合中的集,现在我们要说一下关于集合的映射:

Java类库为映射提供了两个通用的实现,HashMap和TreeMap。这两个类都实现了Map接口。 散列映射对建进行散列,树映射用键的整体顺序对元素进行排序,并将其组织成搜索树。散列和比较函数只能用于键 ,与键关联的值不能进行散列或比较。

Tips:
每当往映射中添加元素对象时,必须提供一个键值。
每当想要检索一个对象时,必须使用一个键。
如果在映射中没有与给定建对应的消息,get将会返回null。
remove方法用于从映射中删除给定键对应的元素。
size方法用于返回映射中的元素数。
要想迭代处理映射的键和值,最容易的方法是forEach方法。可以提供一个接受键的lambda表达式。

键必须是唯一的! 。不能对同一个键存放两个值。如果对同一个键两次调用put方法,第二个值就会取代第一个值。

这里有个很实用的技巧,更新映射项
现在我们考虑一种情况,即键第一次出现,我们要使用一个映射统计一个单词在文件中出现的频度,也就是说,看到一个单词时,我们将计数器增一,如下所示:

counts.put(word,counts(word)+1);

这是可以的,但是有一种很危险的情况!就是第一次看见word时,在这种情况下,get会犯回一个NullPointerException的异常。

对于这个问题,我们有一个简单的补救,可以使用getOrDefault方法:

conuts.put(word,counts.getOrDefault(word,0) + 1);

还有一种方法,是手先调用putIfAbsent方法。只有当键原先存在时才会放入一个值。

counts.putIfAbsent(word,0);
counts.put(word,counts.get(word) + 1);

但是这仍不是最好的方法,merge方法可以简化这个常见的操作。如果原先键不存在,下面的调用:

counts.merge(word,1,Integer::sum);

这行代码的含义是,把word和1关联,否则使用Integer::sum函数组合原值和1。

Part2
我们上面简单介绍了一些关于映射的知识,不过Java的集合框架本身并不认为映射本身是一个集合。值得注意的是,我们可以得到映射的视图——这是实现了Collection接口或某个子接口的对象。

有三种视图:建集、值集 和 键/值对集 。键 和 键/值对 可以组成一个集。下面的方法会分别返回这三个视图:

Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K,V>> entrySet()

需要说明的是,keySet不是HashSet和TreeSet这类东西,而是实现了Set接口的另外某个类的对象。而Set接口扩展了Colletion接口。因此,可以像使用集合一样使用keySet。
例如,可以枚举一个映射所有的键:

Set<String> keys = map.keySet();
for(String key:keys)
{
    do somethings with key
}

如果想同时产看键和值,可以通过枚举条目来避免查找值!如下:

for(Map.Entry<String, Employee>entry : staff.entrySet())
{
    String k = entry.getKey();
    Employee v = entry.getValue();
    do something with k,v
}

奥!对了,如果在键集视图上调用迭代器的remove方法,实际上会从映射中删除这个建和他关联的值!但是却不能向建集视图中增加元素!

Part3
现在开始我们将会介绍在集合类库中的几个专用的映射类。
1.弱散列映射
设计弱散列映射(WeakHashMap)类是为了解决一个关于回收的问题。假设一种情况,如果存在一个值,对应键已经不再使用了,将会出现什么情况呢?也就是说,某个键的最后一次引用已经消亡,不再有任何途径引用这个值的对象了。但是,由于程序中任何部分再出现这个键,仿佛被忘记了,就无法remove删除了。 更重要的是java虚拟机的垃圾回收机制,并不能删除它!。原因是:垃圾回收器跟踪的是活动的对象。只要映射对象是活动的,其中所有的桶也是活动的,它们就不能被回收! 为了解决这个问题,就诞生了我们的WeakHashMap类:当对键的唯一引用来自散列条目时,这一数据结构将与垃圾回收器协同工作一起删除键/值对。

2.链接散裂集与映射
LinkedHashSet和LinkedHashMap类,用来记住插入元素项的顺序。这样就可以避免在散列表中的项从变面上看是随机排列的。
我们举一个例子,之前说个散列表的迭代器是随机枚举键的,但是链接散列集就不一样:

Map<String,Employee> staff = new LinkedHashMap<>();
staff.put("144-25-5464", new Employee("Amy Lee"));
staff.put("567-24-2546", new Employee("Harry Hacker"));
staff.put("157-62-7935", new Employee("Gary Cooper"));
staff.put("456-62-5527", new Employee("Francesca Cruz"));

然后调用迭代器staff.keySet().iterator()
你会发现:144-25-5464,567-24-2546,157-62-7935,456-62-5527。
如果调用的是staff.values().iterator()
你会发现:Amy Lee,Harry Hacker,Gary Cooper,Francesca Cruz

显然,现在他们的迭代都是存在顺序地,不是随机的了!链接散列映射将用访问顺序,而不是插入顺序,对映射条目进行迭代。每次抵用get或put,收到影响的条目将从当前位置删除,并放到条目链表的尾部。

3.枚举集与映射
EmunSet是一个枚举类型元素集的高效实现。由于枚举类型只有有限个实例,所以EnumSet内部用位序列实现。如果对性的值在集中,则相应的位被设置为1。EnumSet,快!

4.标识散列映射
类IdentityHashMap有特殊作用。在这个类中,键的散列值不是用hashCode函数计算的,而是用System.identityHahCode方法计算的。这是Object.hashCode方法根据对象的内存地址来计算散列码时所使用的方法。而且在两个对象进行比较时,IdentityHashMap类使用==,而不使用equals。
也就说不同的键对象,即使内容相同,也被视为不同的对象。在实现对象遍历算法时,这个类非常有用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章