目录
Interface Set<E>
观察API文档我们发现:
public interface Set<E>
extends Collection<E>不包含重复元素的集合。 更正式地,集合不包含一对元素
e1
和e2
,使得e1.equals(e2)
,并且最多一个空元素。 正如其名称所暗示的那样,这个接口模拟了数学集抽象。Set接口除了继承自Collection接口的所有构造函数的合同以及add,equals和hashCode方法的合同外 , 还 增加了其他规定。 其他继承方法的声明也包括在这里以方便。 (伴随这些声明的规范已经量身定做Set接口,但它们不包含任何附加的规定。)
构造函数的额外规定并不奇怪,所有构造函数都必须创建一个不包含重复元素的集合(如上所定义)。
注意:如果可变对象用作设置元素,则必须非常小心。 如果对象的值以影响equals比较的方式更改,而对象是集合中的元素, 则不指定集合的行为。 这种禁止的一个特殊情况是,一个集合不允许将其本身作为一个元素。
一些集合实现对它们可能包含的元素有限制。 例如,一些实现禁止空元素,有些实现对元素的类型有限制。 尝试添加不合格元素会引发未经检查的异常,通常为NullPointerException或ClassCastException 。 尝试查询不合格元素的存在可能会引发异常,或者可能只是返回false; 一些实现将展现出前者的行为,一些实现将展现出后者。 更一般来说,尝试对不符合条件的元素的操作,其完成不会导致不合格元素插入到集合中,可能会导致异常,或者可能会成功执行该选项。 此异常在此接口的规范中标记为“可选”。
此接口是成员Java Collections Framework 。
Set接口
一个不包含重复元素的 collection,就是元素唯一且元素无序(存储和取出不一致)的集合。
存储字符串并遍历,参考代码:
/*
存储字符串并遍历
*/
import java.util.HashSet;
public class SetDemo {
public static void main(String[] args) {
//定义字符串集合
HashSet<String> arr = new HashSet<>();
//添加元素到集合
arr.add("hello");
arr.add("world");
arr.add("java");
arr.add("bigdata");
arr.add("hadoop");
arr.add("hello");
arr.add("hello");
arr.add("java");
arr.add("spark");
arr.add("flink");
arr.add("world");
arr.add("hadoop");
for (String s : arr) {
System.out.println(s);
}
}
}
输出结果:
flink
world
java
bigdata
spark
hello
hadoop
HashSet
查看API文档我们知道:
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 。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。
通过观察API文档我们得出结论:
- 类允许为null
- 不保证set的迭代顺序
- 底层数据结构是哈希表(元素是链表的数组)
- 哈希表依赖于哈希值存储
- 添加功能底层依赖两个方法:
int hashCode()
boolean equals(Object obj)
存储自定义对象并遍历,参考代码:
创建Student2对象:
import java.util.Objects;
public class Student2 {
private String name;
private int age;
public Student2() {
}
public Student2(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student2{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student2 student2 = (Student2) o;
return age == student2.age &&
Objects.equals(name, student2.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
创建HashSetDemo测试类:
/*
存储自定义对象并遍历
*/
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
//创建集合对象
HashSet<Student2> hashSet = new HashSet<>();
//创建学生对象
Student2 s1 = new Student2("刘德华", 45);
Student2 s2 = new Student2("刘德华", 45);
Student2 s3 = new Student2("郭富城", 50);
Student2 s4 = new Student2("张学友", 60);
//将学生对象添加到集合中
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
hashSet.add(s4);
//使用增强for遍历集合
fo