一、Collection接口

List是有序的队列,List中可以有重复的元素。Set是数学概念中的集合,Set中没有重复元素。

二、List集合源码解析

1. Arraylist

数据结构:ArrayList是一个动态数组,由数组实现。

特点:能够自动扩展大小。

2. LinkList

数据结构:LinkedList(双向链表)通过一个Node内部类实现的这种链表结构。能存储null值。

跟arrayList相比较,就真正的知道了,LinkedList在删除和增加等操作上性能好,而ArrayList在查询的性能上好。从源码中看,它不存在容量不足的情况(不需要指定元素个数,能链多长就链多长)。linkedList不光能够向前迭代,还能像后迭代,并且在迭代的过程中,可以修改值、添加值、还能移除值。linkedList不光能当链表,还能当队列使用,这个就是因为实现了Deque接口。

3. Vector

矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。

4. Stack

栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。

5. List总结

a. 各自的使用场景:

1. 对于需要快速插入,删除元素,应该使用LinkedList。

2. 对于需要快速随机访问元素,应该使用ArrayList。

3. 对于“单线程环境”,List仅仅只会被单个线程操作时,此时应该使用非同步的类(如ArrayList、LinkedList)。

4. 对于“多线程环境”,且List可能同时被多个线程操作时,此时,应该使用同步的类(如Vector)。

b. 问答

1. 为什么LinkedList中插入元素很快,而ArrayList中插入元素很慢?(删除元素”与“插入元素”的原理类似)

答:因为LinkedList是基于双向链表实现的,插入和删除元素时只需要修改指针指向即可,时间复杂度为O(1);而ArrayList是基于动态数组实现的,插入和删除元素时需要移动大量元素,时间复杂度为O(n)。

2. 为什么LinkedList中随机访问很慢,而ArrayList中随机访问很快?

答:因为LinkedList是基于双向链表实现的,随机访问需要从头节点开始遍历,时间复杂度为O(n);而ArrayList是基于动态数组实现的,随机访问可以通过索引直接访问,时间复杂度为O(1)。

介绍 “(n - 1) & hash”,就是通过它来获取指定key在数组中的位置)

数据结构:(数组加链表加红黑树)

hashMap中的常量:

特点:

1. 数据结构:在JDK1.8以前是一个链表散列这样一个数据结构,而在JDK1.8以后是一个数组加链表加红黑树的数据结构。

2. hashMap是一个能快速通过key获取到value值的一个集合。

问答:

- hashMap它是何时、如何扩容的?

- hashMap初始值应该怎么设置?

- hashMap是如何储存数据的?

- hashMap它查找数据为什么那么快?

- 算出元素在数组中的位置索引:“(n - 1) & hash”,它是怎么计算的?

2. LinkedHashMap

数据结构:(数组加双向链表加红黑树)

特点:

在实现上,LinkedHashMap 很多方法直接继承自 HashMap,仅为维护双向链表覆写了部分方法。

四、fail-fast原理

(主要看评论)

原因:每次我们队集合进行增删改操作的时候,都会先运行下面这个函数,若“modCount 不等于 expectedModCount”,则抛出ConcurrentModificationException异常,产生fail-fast事件。

解决:使用CopyOnWriteArrayList就不会报错了。

释:CopyOnWriteArrayList直接继承了List接口,是自己实现Iterator。原理:CopyOnWriteArrayList的add、set、remove等会改变原数组的方法中,都是先copy一份原来的array,再在copy数组上进行add、set、remove操作,这就才不影响COWIterator那份数组。

问答:

- 在CopyOnWriteArrayList的迭代器中没有比较两个modCount,如果在ArrayList中不比较两个modCount也不会爆那个异常。所以报不报异常就看有没有比较modCount。因为这个异常是比较不一致以后人为主动扔出来的首先我们要知道ArrayList比较的目的是什么?保证数据一致性啊。现在的问题是为什么CopyOnWriteArrayList可以不比较modCount也能保证数据一致性?

getArray() 返回的数组类型是 volatile,这意味着在多线程环境下,内存一致性会被强制执行。

在多线程编程中,volatile 关键字用于确保变量在不同线程之间保持可见性。当一个变量被声明为 volatile 时,编译器会禁止对该变量进行重排序,以确保它在任何时候都按照代码的预期顺序进行读写。这有助于避免因为缓存不一致或其他硬件相关问题导致的问题。

在这个例子中,由于 getArray() 返回的数组类型是 volatile,我们可以确保在多线程环境下,对该数组的读取和写入操作会遵循内存一致性规则。这意味着在一个线程修改数组内容时,其他线程能够立即看到这个变化。

需要注意的是,虽然使用 volatile 可以确保内存一致性,但它并不解决所有可能的并发问题。为了编写正确的多线程程序,还需要考虑其他同步原语(如互斥锁、原子操作等)以及避免数据竞争和死锁等问题。