LinkedHashSet
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。
此链接列表定义了迭代顺序,即按照将元素插入到集合中的顺序(插入顺序)进行迭代。
注意,插入顺序不受在集合中重新插入的元素的影响。、
(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到集合 s 中。)
此实现可以让客户免遭未指定的、由 HashSet 提供的通常杂乱无章的排序工作,
而又不致引起与 TreeSet 关联的成本增加。使用它可以生成一个与原来顺序相同的集合副本,
并且与原集合的实现无关:
void foo(Set m) {
Set copy = new LinkedHashSet(m);
...
}
如果模块通过输入得到一个集合,复制这个集合,然后返回由此副本决定了顺序的结果,
这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)
此类提供所有可选的 Set 操作,并且允许 null 元素。与 HashSet 一样,它可以为基本操作(add、contains 和 remove)提供稳定的性能,
假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比 HashSet 稍逊一筹,
不过,这一点例外:LinkedHashSet迭代所需时间与集合的大小成正比,而与容量无关。
HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量成正比。
链接的哈希集合有两个影响其性能的参数:初始容量和加载因子。它们与 HashSet 中的定义极其相同。
注意,为初始容量选择非常高的值对此类的影响比对HashSet要小,因为此类的迭代时间不受容量的影响。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希集合,而其中至少一个线程修改了该集合,
则它必须 保持外部同步。这一般通过对自然封装该集合的对象进行同步操作来完成。
如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装”该集合。
最好在创建时完成这一操作,以防止意外的非同步访问:
Set s = Collections.synchronizedSet(new LinkedHashSet(...));
此类的 iterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果对集合进行修改,
除非通过迭代器自身的移除方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。
因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何强有力的保证。
快速失败迭代器尽最大努力抛出 ConcurrentModificationException。
因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
注意1:允许 null 元素,但最多只能加一个。如果LinkedHashSet已经有null元素,
试图再加null,不会成功,也不会抛异常。
注意2:实现不同步的,不是线程安全的。
注意3:此类的iterator方法返回的迭代器是快速失败 的:在创建迭代器之后,如果对集合进行修改,
除非通过迭代器自身的 remove 方法,否则在任何时间以任何方式对其进行修改,
Iterator 都将抛出 ConcurrentModificationException
注意4:iterator()返回的迭代器,里面的元素是插入的顺序排序的(先插入的在前面).
注意5:关于对象的相等和HashSet一样。
具体可以查考HashSet和《hashcode()和equals()及HashSet判断对象相等》
注意6:文档中说”为初始容量选择非常高的值对此类的影响比对HashSet要小“,应是指迭代时间的影响。
注意7:文档中以下描述
”(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到集合 s 中。)“
不正确。 事实是:当试图添加一个重复元素到LinkedHashSet时,新元素并不会把旧元素替换掉,
而只是新元素不会添加到LinkedHashSet不会抛异常。
当然如果插入重复元素,是以第一次插入的顺序为准。例1就是个很好的证明。
例1:
import java.util.LinkedHashSet;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
LinkedHashSet<People> set=new LinkedHashSet();
set.add(new People("robin",1,21));
set.add(new People("hb",2,20));
set.add(new People("harry",9,30));
set.add(null);
People p4=new People("robin",4,25);
set.add(p4);
set.add(new People("yp",5,28));
set.add(new People("yp2",8,28));
set.add(null);
set.add(null);
for(People p:set)
System.out.println(p);
}
}
class People{
String name;
int id;
int age;
public People(String name,int id)
{
this(name,id,0);
}
public People(String name,int id,int age)
{
this.name=name;
this.id=id;
this.age=age;
}
public String toString()
{
return id+name+age;
}
public boolean equals(Object o)
{
if(o==null)
return false;
if(!(o instanceof People))
return false;
People p=(People)o;
boolean res=name.equals(p.name);
if(res)
System.out.println("name "+name+" is double");
else
System.out.println(name+" vS "+p.name);
return res;
}
public int hashCode()
{
return name.hashCode();
}
}
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。
此链接列表定义了迭代顺序,即按照将元素插入到集合中的顺序(插入顺序)进行迭代。
注意,插入顺序不受在集合中重新插入的元素的影响。、
(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到集合 s 中。)
此实现可以让客户免遭未指定的、由 HashSet 提供的通常杂乱无章的排序工作,
而又不致引起与 TreeSet 关联的成本增加。使用它可以生成一个与原来顺序相同的集合副本,
并且与原集合的实现无关:
void foo(Set m) {
Set copy = new LinkedHashSet(m);
...
}
如果模块通过输入得到一个集合,复制这个集合,然后返回由此副本决定了顺序的结果,
这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)
此类提供所有可选的 Set 操作,并且允许 null 元素。与 HashSet 一样,它可以为基本操作(add、contains 和 remove)提供稳定的性能,
假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比 HashSet 稍逊一筹,
不过,这一点例外:LinkedHashSet迭代所需时间与集合的大小成正比,而与容量无关。
HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量成正比。
链接的哈希集合有两个影响其性能的参数:初始容量和加载因子。它们与 HashSet 中的定义极其相同。
注意,为初始容量选择非常高的值对此类的影响比对HashSet要小,因为此类的迭代时间不受容量的影响。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希集合,而其中至少一个线程修改了该集合,
则它必须 保持外部同步。这一般通过对自然封装该集合的对象进行同步操作来完成。
如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装”该集合。
最好在创建时完成这一操作,以防止意外的非同步访问:
Set s = Collections.synchronizedSet(new LinkedHashSet(...));
此类的 iterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果对集合进行修改,
除非通过迭代器自身的移除方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。
因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何强有力的保证。
快速失败迭代器尽最大努力抛出 ConcurrentModificationException。
因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
注意1:允许 null 元素,但最多只能加一个。如果LinkedHashSet已经有null元素,
试图再加null,不会成功,也不会抛异常。
注意2:实现不同步的,不是线程安全的。
注意3:此类的iterator方法返回的迭代器是快速失败 的:在创建迭代器之后,如果对集合进行修改,
除非通过迭代器自身的 remove 方法,否则在任何时间以任何方式对其进行修改,
Iterator 都将抛出 ConcurrentModificationException
注意4:iterator()返回的迭代器,里面的元素是插入的顺序排序的(先插入的在前面).
注意5:关于对象的相等和HashSet一样。
具体可以查考HashSet和《hashcode()和equals()及HashSet判断对象相等》
注意6:文档中说”为初始容量选择非常高的值对此类的影响比对HashSet要小“,应是指迭代时间的影响。
注意7:文档中以下描述
”(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到集合 s 中。)“
不正确。 事实是:当试图添加一个重复元素到LinkedHashSet时,新元素并不会把旧元素替换掉,
而只是新元素不会添加到LinkedHashSet不会抛异常。
当然如果插入重复元素,是以第一次插入的顺序为准。例1就是个很好的证明。
例1:
import java.util.LinkedHashSet;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
LinkedHashSet<People> set=new LinkedHashSet();
set.add(new People("robin",1,21));
set.add(new People("hb",2,20));
set.add(new People("harry",9,30));
set.add(null);
People p4=new People("robin",4,25);
set.add(p4);
set.add(new People("yp",5,28));
set.add(new People("yp2",8,28));
set.add(null);
set.add(null);
for(People p:set)
System.out.println(p);
}
}
class People{
String name;
int id;
int age;
public People(String name,int id)
{
this(name,id,0);
}
public People(String name,int id,int age)
{
this.name=name;
this.id=id;
this.age=age;
}
public String toString()
{
return id+name+age;
}
public boolean equals(Object o)
{
if(o==null)
return false;
if(!(o instanceof People))
return false;
People p=(People)o;
boolean res=name.equals(p.name);
if(res)
System.out.println("name "+name+" is double");
else
System.out.println(name+" vS "+p.name);
return res;
}
public int hashCode()
{
return name.hashCode();
}
}