Set集合
-
public interface Set<E>
extends Collection<E>不包含重复元素的集合。更正式地,集合不包含一对元素
e1
和e2
,使得e1.equals(e2)
,并且最多一个空元素。正如其名称所暗示的那样,这个接口模拟了数学集抽象。Set
接口除了继承自Collection
接口的所有构造函数的合同
以及add,equals
和hashCode
方法的合同外
,还
增加
了其他规定。 其他继承方法的声明也包括在这里以方便。 (伴随这些声明的规范已经量身定做Set
接口,但它们不包含任何附加的规定。)构造函数的额外规定并不奇怪,所有构造函数都必须创建一个不包含重复元素的集合(如上所定义)。
注意:如果可变对象用作设置元素,则必须非常小心。 如果对象的值以影响
equals
比较的方式更改,而对象是集合中的元素,则不
指定集合的行为。 这种禁止的一个特殊情况是,一个集合不允许将其本身作为一个元素。一些集合实现对它们可能包含的元素有限制。 例如,一些实现禁止空元素,有些实现对元素的类型有限制。 尝试添加不合格元素会引发未经检查的异常,通常为
NullPointerException
或ClassCastException
。 尝试查询不合格元素的存在可能会引发异常,或者可能只是返回false; 一些实现将展现出前者的行为,一些实现将展现出后者。 更一般来说,尝试对不符合条件的元素的操作,其完成不会导致不合格元素插入到集合中,可能会导致异常,或者可能会成功执行该选项。 此异常在此接口的规范中标记为“可选”。Set接口特点:
1、不允许存储重复的元素。
2、没有索引,没有带索引的方法,也不能使用普通的for循环遍历。
方法与collecion一致。
HashSet
-
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable此类实现
Set
接口,由哈希表(实际为HashMap
实例)支持。对集合的迭代次序不作任何保证;特别是,它不能保证订单在一段时间内保持不变。这个类允许null
元素。这个类提供了基本操作
(add,remove,contains
和size)
固定的时间性能,假定哈希函数将分散的桶中正确的元素。 迭代此集合需要与HashSet
实例的大小(元素数量)和后台HashMap
实例(桶数)的“容量”的总和成比例
的时间。 因此,如果迭代性能很重要,不要将初始容量设置得太高(或负载因子太低)是非常重要的。请注意,此实现不同步。 如果多个线程并发访问哈希集,并且至少有一个线程修改该集合,那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在,那么该集合应该使用
Collections.synchronizedSet
方法“包装”。 这最好在创建时完成,以防止对该集合的意外不同步访问:Set s = Collections.synchronizedSet(new HashSet(...));
该类
iterator
方法返回的迭代器是故障快速的 :如果集合在迭代器创建之后的任何时间被修改,除了通过迭代器自己的remove
方法之外,迭代器会抛出一个ConcurrentModificationException
。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力投入
ConcurrentModificationException
。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。
-
-
boolean
add(E e)
将指定的元素添加到此集合(如果尚未存在)。void
clear()
从此集合中删除所有元素。Object
clone()
返回此HashSet
实例的浅层副本:元素本身不被克隆。boolean
contains(Object o)
如果此集合包含指定的元素,则返回true
。boolean
isEmpty()
如果此集合不包含元素,则返回true
。Iterator
iterator()
返回此集合中元素的迭代器。boolean
remove(Object o)
如果存在,则从该集合中删除指定的元素。int
size()
返回此集合中的元素数(其基数)。Spliterator
spliterator()
在此集合中的元素上创建late-binding和故障快速Spliterator
。
-
HashSet接口特点:
1、不允许存储重复的元素。
2、没有索引,没有带索引的方法,也不能使用普通的for循环遍历。
3、是一个无序的集合,存储元素和取出元素的顺序有可能不一致。
4、底层是一个哈希表结构(查询速度非常快)。
public class MyHashSet {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(1);//是否存储?
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println("---------------------");
for (Integer integer : set) {
System.out.println(integer);
}
}
}
运行结果:
1
2
3
---------------------
1
2
3
HashSet存储自定义类型元素:重写hashcode和equals方法
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person person = (Person) o;
return Objects.equals(name, person.name) &&
Objects.equals(age, person.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//创建hashset集合存储person
HashSet<Person> hashSet = new HashSet<Person>();
Person p1 = new Person("haha", 18);
Person p2 = new Person("haha", 18);
Person p3 = new Person("haohoa", 19);
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println(p1.equals(p2));
hashSet.add(p1);
hashSet.add(p2);
hashSet.add(p3);
for (Person person : hashSet) {
System.out.println(person);
}
Iterator<Person> iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
运行结果:
99039841
99039841
true
Person{name='haha', age=18}
Person{name='haohoa', age=19}
Person{name='haha', age=18}
Person{name='haohoa', age=19}
LinkedHashSet
-
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, Serializable底层是一个哈希表(数组 + 链表/红黑树)+ 链表;多了一条链表(记录元素的存储顺序),保证元素的有序性。
哈希表和链表实现了
Set
接口,具有可预测的迭代次序。 这种实现不同于HashSet,
它维持于所有条目的运行双向链表。 该链表定义了迭代排序,它是将元素插入集合(插入顺序 ) 的顺序 。 请注意,如果一个元件被重新插入到组插入顺序不受影响 。 (元件e
重新插入一组s
如果当s.contains(e)
将返回true
之前立即调用s.add(e)
被调用。)此实现可以让客户从提供的指定,通常杂乱无章的排序
HashSet
,而不会导致与其相关的成本增加TreeSet
。 它可以用于生成与原始文件具有相同顺序的集合的副本,而不管原始集的实现:void foo(Set s) {
Set copy = new LinkedHashSet(s);
...
}如果模块在输入上进行设置,复制它,并且稍后返回其顺序由该副本确定的结果,则此技术特别有用。(客户一般都喜欢以相同的顺序返回事情。)
该类提供了所有可选的
Set
操作,并允许null元素。 像HashSet,
它提供了基本操作(add,contains
和remove)
稳定的性能,假定散列函数散桶中适当的元件。 性能可能略低于HashSet
,由于维护链表的额外费用,但有一个例外:LinkedHashSet的迭代
需要与集合的大小成比例的
时间,无论其容量如何。 HashSet的迭代
可能更昂贵,需要与其容量成比例的时间。链接哈希集具有影响其性能的两个参数: 初始容量和负载因子 。 它们的定义精确到
HashSet
。 但是请注意,该惩罚为初始容量选择非常高的值是该类比HashSet
不太严重的,因为迭代次数对于这个类是由容量不受影响。请注意,此实现不同步。 如果多个线程同时访问链接的散列集,并且至少有一个线程修改该集合,那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在,则应该使用
Collections.synchronizedSet
方法“包装”。 这最好在创建时完成,以防止对该集合的意外不同步访问:Set s = Collections.synchronizedSet(new LinkedHashSet(...));
该类
iterator
方法返回的迭代器是故障快速的 :如果在创建迭代器之后的任何时间对该集合进行了修改,除了通过迭代器自己的remove
方法之外,迭代器将会抛出一个ConcurrentModificationException
。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力投入
ConcurrentModificationException
。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。