1.官方介绍
Hash表和链表实现了Set接口,具有可预测的迭代顺序。该实现与HashSet的不同之处在于它维护了一个贯穿其所有条目的双向链表。该链表定义了迭代顺序,即元素插入集合的顺序(插入顺序)。注意,如果一个元素重新插入到集合中,插入顺序不受影响。当s .包含( e )在调用之前立即返回true时调用(如果s . add ( e ) ,则元素e重新插入到集合s中)。
这种实现使其客户端免受HashSet提供的未指定的、通常是混乱的排序,而不会增加与TreeSet相关的成本。它可以用来产生一个集合的副本,该集合与原集合具有相同的顺序,而不管原集合的实现方式如何:void foo(Set s) {
Set copy = new LinkedHashSet(s);
...
}
这种技术特别有用,如果一个模块在输入上取一个集合,复制它,然后返回由复制的顺序决定的结果。(客户一般赞赏以相同的顺序返回的东西。)
该类提供了所有可选的Set操作,并且允许空元素。与Hash Set一样,它为基本操作(添加、包含和移除)提供了常数时间性能,假设哈希函数在桶之间适当分散元素。性能很可能只是略低于Hash Set,因为增加了维护链表的费用,但有一个例外:在Linked Hash Set上迭代需要与集合大小成比例的时间,而不管其容量如何。在HashSet上进行迭代可能比较昂贵,需要的时间与其容量成正比。一个链接哈希集有两个影响其性能的参数:初始容量和负载因子。对于HashSet而言,它们是精确定义的。但需要注意的是,选择过高的初始容量值对该类的惩罚不如Hash Set严重,因为该类的迭代次数不受容量的影响。
注意,这种实现并不同步。如果多个线程同时访问一个链接哈希集,并且至少有一个线程修改该哈希集,则必须对其进行外部同步。这通常是通过对一些自然封装集合的对象进行同步来完成的。如果不存在这样的对象,则应该使用Collections . synchronoused Set方法对集合进行"包装"。这最好在创建时完成,以防止意外的不同步访问集合:Set s = Collections.synchronizedSet(new LinkedHashSet(...));
该类迭代器方法返回的迭代器是快速失败的:如果在迭代器创建后的任何时候修改了集合,除了通过迭代器自己的移除方法之外,迭代器将抛出Concurrent ModificationException异常。因此,在面对并发修改时,迭代器能够快速、干净地失效,而不是冒着在未来不确定时刻发生任意、非确定性行为的风险。
需要注意的是,迭代器的快速失效行为是无法保证的,因为通常情况下,在存在不同步的并发修改的情况下,无法做出任何硬性保证。快速迭代器在尽力而为的基础上抛出Concurrent ModificationException。因此,编写一个依赖于该异常的程序来保证其正确性是错误的:迭代器的失效快行为应该只用于检测bug。
2.LikedHashSet的特性
LikedHashSet是对HashSet的升级,主要是解决HashSet存放元素无序的问题,LikedHashSet底层就是LikedHashMap
3. 手写LikedHashSet
package com.example.list.list;
import java.util.LinkedHashMap;
//手写LinkedHashSet
public class LinkedHashSet<E> {
private LinkedHashMap<E, Object> linkedHashMap;
private static final Object PRESENT = new Object();
public LinkedHashSet() {
linkedHashMap = new LinkedHashMap();
}
public void add(E e){
linkedHashMap.put(e, PRESENT);
}
@Override
public String toString() {
return "LinkedHashSet{" +
"linkedHashMap=" + linkedHashMap +
'}';
}
}