集合源码解析

  1. Arraylist

1)arrayList可以存放null。
2)arrayList本质上就是一个elementData数组。
3)arrayList区别于数组的地方在于能够自动扩展大小,其中关键的方法就是gorw()方法,每次扩容1.5倍
4)arrayList中removeAll(collection c)和clear()的区别就是removeAll可以删除批量指定的元素,而clear是全是删除集合中的元素。
5)arrayList由于本质是数组,所以它在数据的查询方面会很快,而在插入删除这些方面,性能下降很多,有移动很多数据才能达到应有的效果
6)arrayList实现了RandomAccess,所以在遍历它的时候推荐使用for循环。
7)arrayList线程不安全

  1. LinkedList
    1)linkedList本质上是一个双向链表,通过一个Node内部类实现的这种链表结构。
    2)能存储null值
    3)跟arrayList相比较,就真正的知道了,LinkedList在删除和增加等操作上性能好,而ArrayList在查询的性能上好
    4)从源码中看,它不存在容量不足的情况
    5)linkedList不光能够向前迭代,还能像后迭代,并且在迭代的过程中,可以修改值、添加值、还能移除值。
    6)linkedList不光能当链表,还能当队列使用,这个就是因为实现了Deque接口。
List list1 = new LinkedList();
        list1.add(1);
        list1.add(2);
        list1.add(8);
        list1.add(4);
        list1.stream().forEach(i -> {
            System.out.println(i);
        });
  1. Vector和Stack
    3.1、Vector总结(通过源码分析)
      1)Vector线程安全是因为它的方法都加了synchronized关键字,扩容每次都是两倍,其余和ArrayList一致
      2)Vector的本质是一个数组,特点能是能够自动扩增,扩增的方式跟capacityIncrement的值有关
      3)它也会fail-fast,还有一个fail-safe两个的区别在下面的list总结中会讲到

3.2、Stack的总结
  1)对栈的一些操作,先进后出
  2)底层也是用数组实现的,因为继承了Vector
  3)也是线程安全的

  1. List总结
    4.1 arrayList和LinkedList区别
    arrayList底层是用数组实现的顺序表,是随机存取类型,可自动扩增,并且在初始化时,数组的长度是0,只有在增加元素时,长度才会增加。默认是10,不能无限扩增,有上限,在查询操作的时候性能更好
    LinkedList底层是用链表来实现的,是一个双向链表,注意这里不是双向循环链表,顺序存取类型。在源码中,似乎没有元素个数的限制。应该能无限增加下去,直到内存满了在进行删除,增加操作时性能更好。
    两个都是线程不安全的,在iterator时,会发生fail-fast

4.2 arrayList和Vector的区别
arrayList线程不安全,在用iterator,会发生fail-fast
Vector线程安全,因为在方法前加了Synchronized关键字。也会发生fail-fast

4.3 为什么现在都不提倡使用vector了
1)vector实现线程安全的方法是在每个操作方法上加锁,这些锁并不是必须要的,在实际开发中,一般都是通过锁一系列的操作来实现线程安全,也就是说将需要同步的资源放一起加锁来保证线程安全,
2)如果多个Thread并发执行一个已经加锁的方法,但是在该方法中,又有vector的存在,vector本身实现中已经加锁了,那么相当于锁上又加锁,会造成额外的开销,
3)就如上面第三个问题所说的,vector还有fail-fast的问题,也就是说它也无法保证遍历安全,在遍历时又得额外加锁,又是额外的开销,还不如直接用arrayList,然后再加锁呢。
总结:Vector在你不需要进行线程安全的时候,也会给你加锁,也就导致了额外开销,所以在jdk1.5之后就被弃用了,现在如果要用到线程安全的集合,都是从java.util.concurrent包下去拿相应的类。

  1. HashMap
    5.1 jdk1.8以前的数据结构和存储原理
    1)链表散列:通过数组和链表结合在一起使用,就叫做链表散列。这其实就是hashmap存储的原理图。
    在这里插入图片描述
    2)HashMap的数据结构和存储原理
    HashMap的数据结构就是用的链表散列。那HashMap底层是怎么样使用这个数据结构进行数据存取的呢?分成两个部分:
    第一步:HashMap内部有一个entry的内部类,其中有四个属性,我们要存储一个值,则需要一个key和一个value,存到map中就会先将key和value保存在这个Entry类创建的对象中。
static class Entry<K,V> implements Map.Entry<K,V> {
            final K key;    //就是我们说的map的key
            V value;    //value值,这两个都不陌生
            Entry<K,V> next;//指向下一个entry对象
            int hash;//通过key算过来的你hashcode值。

在这里插入图片描述
第二步:构造好了entry对象,然后将该对象放入数组中,如何存放就是这hashMap的精华所在了。
大概的一个存放过程是:通过entry对象中的hash值来确定将该对象存放在数组中的哪个位置上,如果在这个位置上已经有entry元素了,则通过链表来存储这个元素,此时这个数组的位置上有两个通过链表形式存储的entry元素
在这里插入图片描述
3)Hash存放元素的过程
通过key、value封装成一个entry对象,然后通过key的值来计算该entry的hash值,通过entry的hash值和数组的长度length来计算出entry放在数组中的哪个位置上面,每次存放都是将entry放在链表的第一个位置。

5.2 JDK1.8后HashMap的数据结构
在这里插入图片描述
上图很形象的展示了HashMap的数据结构(数组+链表+红黑树),桶中的结构可能是链表,也可能是红黑树,红黑树的引入是为了提高效率,也就是说1.8之后,hashmap还是数组+链表的结构,只不过当链表的节点数大于8,就会将链表转成红黑树,下面查看源码:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;

源码所示,当链表的节点大于8,则转成红黑树if (binCount >= TREEIFY_THRESHOLD - 1)

参考链接:https://www.cnblogs.com/zhangyinhua/p/7698642.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值