一、Set介绍: 是一个散列的集合,数据会按照散列值存储的,如两个hello的散列值相同,会存储在同一个地址中,所以看到的就是只有一个hello在集合中了。
1、Set集合有两个主要的实现子类:Hashset和Treeset。hashset是去重复,而Treeset需要实现compareble接口来排序(比较其实是一种变向的去重复,一旦两个对象比较的元素相同,这两个对象只会存一个进去)。
2、Set的特征:
不会出现重复的元素(按照equals 和hashCode 的规则比较是否重复)
Set属于Collection的子接口,拥有Collection的所有的方法;
Set 是散列存储没有位置编号,没有按照编号进行操作的方法。
注意Set<T>如果这个T没有正确重写equals和hashCode方法,会存入重复值。而List<T>即使T正确重写了equals和hsahCode方法,也会存入重复值。
二、Hashset:无序,不排序。
1、底层原理:HashSet底层是哈希表结构的。原理和HashMap相似,HashMap的key集合即为HashSet。
哈希表:
(1)JDK8之前,底层采用数组+链表实现。
HashSet<String> haset1 = new HashSet<>();
创建一个空的hashSet时,底层创建一个默认长度16,默认加载因子0.75的数组,数组名table
(2)JDK8以后,底层进行了优化。由数组+链表+红黑树实现:如果链表长度很长时,新存入的元素,需要通过equals比较的次数就越多,性能就会降低,即链表长度越长,添加元素时,效率越低,jdk1.8对此进行了优化,当链表长度为8时,再次添加元素自动转换成红黑树 ,当链表长度小于8时,自动转为链表。好处 equals比较次数减少,小了往左边比较,大了往右边比较。
2、线程安全问题:
线程不安全。
public static void main(String[] args) {Set<String> hashSet = new HashSet<>();for (int i = 0; i < 10; i++) {new Thread(() -> {//向集合中添加内容hashSet.add(UUID.randomUUID().toString().substring(0, 8));//从集合中获取内容System.out.println(hashSet);}, String.valueOf(i)).start();}}/*Exception in thread "0" java.util.ConcurrentModificationExceptionat java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)at java.util.HashMap$KeyIterator.next(HashMap.java:1469)at java.util.AbstractCollection.toString(AbstractCollection.java:461)at java.lang.String.valueOf(String.java:2994)at java.io.PrintStream.println(PrintStream.java:821)at HashSetDemo.lambda$main$0(HashSetDemo.java:13)at java.lang.Thread.run(Thread.java:748)*/
}
HashSet线程不安全解决方案:
(1) 通过CopyOnWriteArraySet类来解决
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;public class HashSetDemo {public static void main(String[] args) {Set<Object> hashSet = new CopyOnWriteArraySet<>();for (int i = 0; i < 10; i++) {new Thread(() -> {//向集合中添加内容hashSet.add(UUID.randomUUID().toString().substring(0, 8));//从集合中获取内容System.out.println(hashSet);}, String.valueOf(i)).start();}}
}
(2) 通过Collections.synchronizedSet()解决。
三、Treeset:TreeSet并不是根据插入的顺序来排序,而是根据实际的值的大小来排序。
1、底层原理:TreeSet中使用平衡树,更具体的一个红黑树。
2、线程安全问题:线程不安全。解决方案同HashSet。