集合(二)Collection集合Set

一、无序列表Set: 是一个散列的集合,数据会按照散列值存储的,如两个hello的散列值相同,会存储在同一个地址中,所以看到的就是只有一个hello在集合中了。

1、Set集合有两个主要的实现子类:Hashset和Treeset。hashset是去重复,而Treeset需要实现compareble接口来排序(比较其实是一种变向的去重复,一旦两个对象比较的元素相同,这两个对象只会存一个进去)。

2、Set的特征:
不会出现重复的元素(按照equals 和hashCode 的规则比较是否重复)
Set属于Collection的子接口,拥有Collection的所有的方法;
Set 是散列存储没有位置编号,没有按照编号进行操作的方法。

注意Set如果这个T没有正确重写equals和hashCode方法,会存入重复值。而List即使T正确重写了equals和hsahCode方法,也会存入重复值。

二、Hashset:无序,不排序。

1、底层原理:HashSet底层是哈希表结构的。哈希表:

(1)JDK8之前,底层采用数组+链表实现。

HashSet haset1 = new HashSet<>();

创建一个空的hashSet时,底层创建一个默认长度16,默认加载因子0.75的数组,数组名table 

(2)JDK8以后,底层进行了优化。由数组+链表+红黑树实现:如果链表长度很长时,新存入的元素,需要通过equals比较的次数就越多,性能就会降低,即链表长度越长,添加元素时,效率越低,jdk1.8对此进行了优化,当链表长度为8时,再次添加元素自动转换成红黑树 ,当链表长度小于6时,自动转为链表。好处 equals比较次数减少,小了往左边比较,大了往右边比较。

2、线程安全问题:

线程不安全。

public static void main(String[] args) {
        Set 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.ConcurrentModificationException
	at 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线程不安全解决方案: 

三、Treeset:

1、底层原理:

你可能感兴趣的:(Java基础,java)