Collection和Map总结

Collection和Map篇

1、数组和集合的区别

数组集合
长度的区别长度固定长度可变
内容不变数组存储的同一种类型的元素可以存储不同类型的元素
数据类型基础数据和引用数据类型引用数据类型
特点有序,可重复有序,可重复

2、集合和迭代器

迭代器是便利集合的一种方式,依赖于集合而存在的

image-20230219025022677

结论: 遍历集合(Collection)的元素都可以使用iterator iterator实际上就是在遍历集合

3、常用List集合

ArrayList

  • 底层数据结构是数组,线程不安全

  • 允许元素重复和为null

  • 0.5倍扩容

  • 访问速度快

  • 如果所有操作都针对末尾,增删速度比LinkList要快

LinkedList

  • 底层数据结构是链表,线程不安全
  • 操作位置位于中间,增删速度比ArrayList要快

Vector

  • 底层数据结构是数组,线程安全
  • 允许元素重复和为null
  • 每个方法处都声明了synchronized关键字
  • getLast()deleteLast()这两个方法并不是原子性的 多线程执行会报错

CopyOnWriteList

  • 底层线程安全,可以用于高并发读写

  • 在执行add()操作时并不是对原本数组进行追加修改,而是将List复制并交予另外的集合操作,将操作好的集合赋值给新的集合

     public boolean add(E e) {
            // 加锁
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                // 得到原数组的长度和元素
                Object[] elements = getArray();
                int len = elements.length;
                // 复制出一个新数组
                Object[] newElements = Arrays.copyOf(elements, len + 1);
                // 添加时,将新元素添加到新数组中
                newElements[len] = e;
                // 将volatile Object[] array 的指向替换成新数组
                setArray(newElements);
                return true;
            } finally {
                lock.unlock();
            }
        }
    

    ,之后将处理好的新集合进行返回

  • 缺点

    • 数据一致性,CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性
    • 内存占用高

4、常用Set集合

HashSet

  • 底层数据结构是哈希表(哈希表: 每个元素为链表的数组,也可以叫HashMap)+红黑树

  • 非线程同步

TreeSet

  • 有序,不允许为null,内部有comparaTo执行

  • 底层数据结构是红黑树(自平衡的红黑树)

  • 保证元素的排序方式

  • 非线程同步

LinkedHashSet

  • 底层数据结构由HashMap和双向链表组成
  • 非线程同步

5、常用Map集合

image-20230219184517473

HashMap

  • 允许为null

  • 在jdk8中HashMap的底层是:数组+链表(散列表)+红黑树

  • 默认装载因子默认是0.75(数组),无论是初始化打了还是初始化小了对我们HashMap的性能都不好

    • 装载因子初始值打了,可以减少散列表再散列(扩容的次数),但同时会导致散列冲突的可能性变大,避免散列冲突,散列冲突(hash冲突)也是耗性能的一个操作,红黑树会自动触发平衡
    • 装载因子初始值小了,可以减少散列冲突的可能性,但同时扩容的次数可以能就会变频繁,影响性能
  • 初始容量的默认值是16(数组),它也一样,无论初始化大了还是小了,对我们的HashMap都会有影响的

    • 容量过大,那么遍历时我们的速度就会影响

    • 初始容量国小,散列表再扩散(扩容的次数)可能就变得多,扩容耗费性能

LinkedHashMap

  • LinkedHashMap并没有重写HashMap的put方法,而是吧put方法内部的newNode()方法重写了,LinkedHashMap调用父类的put方法,里面回调的是重写后的newNode()

  • LinkedHashMap可以设置两种遍历顺序

    • 访问顺序
    • 插入顺序(默认)
  • LinkHashMap遍历的是内部维护的双向链表,所以说初始容量对LinkedHashMap遍历是不受影响的

  • 允许为null

TreeMap

  • 由于底层是红黑树,那么时间复杂度可以保证为log(n)
  • key不能为null,因为每个key插入时都需要执行comparaTo 方法,对比一方不能为null
  • HashMap中判断key的存储位置,采用的是计算hash值,将数据放入红黑树中,但是在TreeMap中,传入的key会执行comparaTo方法,得出结果1,0,-1, 从而判断该新的数据节点位置,之后采用中序遍历所有数据

HaskTable

  • 同步安全的,锁全部,内部所有方法都加了synchronized关键字
  • hashtable有contains方法,Hashmap改成了containsKeycontainValue

ConcurrentHashMap

  • 底层结构是散列表(数组 + 链表) + 红黑树,和hashMap一样
  • HashTable是将所有的方法进行同步,效率低下,而ConcurrentHashMap作为一个高并发容器,他是通过部分锁定+CAS算法来进行实现线程安全的,CAS算法也可以认为是乐观锁的一种
  • get方法是非阻塞的,无锁的,重写了其中的node类,通过valatile修饰next来实现每次获取最新设置的值
  • ConcurrentHashMap的key和value都不能为null
  • 注解:例如上图 16个数组表示有16个线程组,map初始化内部只会让一个线程进行初始化操作

Hash碰撞

hash碰撞指多个key值计算至同一个bucket桶上了,导致该桶下的链表长度过长,查找时间从O(1)到O(n)

插入put过程

首先,初始化 HashMap(懒加载模式,只有put的时候才会初始化HashMap),提供了有参构造和无参构造。

容器默认的数组大小 initialCapacity 为 16,也可自己设置,HashMap 会根据我们传入的容量计算一个大于等于该容量的最小的2的N次方,例如传 9,容量为16。

1、通过HashMap自己提供的Hash算法算出当前key的hash值

2、通过计算出hash值去调用indexFor方法计算当前对象应该存储在数组的几号位置

3、判断size是否已经达到了当前阈值(当前数组大小 x 负载因子,以初始16为例,则阈值为16 x 0.75),如果没有,继续插入操作;如果已经达到阈值,则先进行数组扩容,将数组长度扩容为原来的2倍

4、将当前对应的Hash,key,value封装成一个Entry,去数组中查找当前位置有没有元素,如果没有,放在这个位置上,如果此位置已经存在链表,那么遍历链表,如果链表上某个节点的key与当前key进行equals比较后结果为true,则把原来节点上的value返回,将当前新的value替换原来的value,如果遍历完链表,没有找到key与当前key与当前key equeals为true的,就把刚才封装的新的Entry中next指向当前链表的始节点,例如图中数组[3]下的[002]位置,002就是计算的hash值

5、默认情况下使用链表节点,当同一个索引位置的节点在新增后达到9个(阈值8),如果此时长度大于64,则会出发链表节点转红黑树节点;如果数组长度小于64,则不会出发链表转红黑树,而是会进行扩容,因为此时数据量还比较小

面试题提升

ArrayList集合加入1万条数据,应该怎么提高效率

ArrayList的默认初始容量为10,要插入大量数据的时候需要不断扩容,而扩容是非常影响性能的。因此,现在明确了10万条数据了,我们可以直接在初始化的时候就设置ArrayList的容量

为什么HashMap中String、Integer这样的包装类适合作为Key

1、String和Integer等这些类都被final修饰,具有不变性;也保证了key的不变性,并且内部重写了equals和hashCode方法,不容易出现hash计算错误
2、String和Integer保证了hash值得不可变性和准确性,有效减少了hash碰撞
3、String和Integer一定重写了equals和hashCode方法

ConcurrentHashMap和Hashtable的区别

HashTable是阻塞模式的,总是能获取最新的更新,好处是当线程A大量更新数据,期间线程B调用get,线程B就会被阻塞,直到线程A更新完毕,坏处是所有调用都要排除,效率比较低

ConcurrentHashMap是非阻塞模式,在更新时会局部锁住某部分数据,但不会把整个表都锁住,同步读取操作则是完全非阻塞的,好处是在保证合理的同步前提下,效率高,坏处就是读取时不能保证反映最近的更新,线程A更新了Map中的数据,期间线程B只能读取到线程A已经更新完成的数据

Array 和 ArrayList 有什么区别?什么时候该用 Array 而不是 ArrayList 呢?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象

Array指定大小后不可变,而ArrayList大小是可变的

Array没有提供ArrayList那么多功能,比如addAll,removeAll和iterator等

参考文档

https://blog.csdn.net/qq_38534524/article/details/123340822

https://reurl.cc/eX954W

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值