文章目录
-
- 1 集合概述
- 2 List
- 3 Set
- 4 Queue
- 5 Map
-
- 5.1 HashMap 和 Hashtable 的区别
- 5.2 HashMap 的底层实现🔥
- 5.3 哈希冲突解决方法有哪些?🔥
- 5.4 HashMap 的 put 方法的具体流程🔥
- 5.5 HashMap 的扩容机制🔥
- 5.6 HashMap 的长度为什么是 2 的幂次方🔥
- 5.7 HashMap 为什么线程不安全?🔥
- 5.8 HashMap一般用什么做Key?为啥String适合做Key呢?
- 5.9 为什么HashMap要用红黑树而不是平衡二叉树?
- 5.10 ConcurrentHashMap 和 Hashtable 的区别
- 5.11 ConcurrentHashMap 线程安全的底层具体实现🔥
- 5.12 ConcurrentHashMap已经用了synchronized,为什么还要用CAS呢?🔥
- 5.13 ConcurrentHashMap 为什么 key 和 value 不能为 null?
- 5.14 HashSet 与 HashMap 的区别?
1 集合概述
1.1 Java 集合概览🔥
Java 集合,也叫作容器,主要是由两大接口派生而来:一个是 Collection
接口,主要用于存放单一元素;另一个是 Map
接口,主要用于存放键值对。对于 Collection
接口,下面又有三个主要的子接口:List
、Set
和 Queue
。
Java 集合框架如下图所示:
List
:存储的元素是有序的、可重复的。常用的实现List的类有LinkedList,ArrayListSet
:存储的元素不可重复的。常用的实现有HashSet,LinkedHashSet和TreeSet。Queue
:按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。常用的实现有 PriorityQueue.Map
:使用键值对(key-value)存储,key 是无序的、不可重复的,value 是无序的、可重复的。主要实现有TreeMap、HashMap、LinkedHashMap、ConcurrentHashMap。
1.2 数组与集合区别
- 数组是固定长度的数据结构,一旦创建长度就无法改变,而集合是动态长度的数据结构,可以根据需要动态增加或减少元素。
- 数组可以包含基本数据类型和对象,而集合只能包含对象。
- 数组可以直接访问元素,而集合需要通过迭代器或其他方法访问元素。
2 List
2.1 ArrayList底层实现原理🔥
- 底层数据结构:ArrayList底层是用动态的数组实现的
- 初始容量:ArrayList初始容量为0,当第一次添加数据的时候才会初始化容量为10
- 扩容逻辑:ArrayList在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组
- 添加逻辑
- 确保数组已使用长度(size)加1之后足够存下下一个数据
- 计算数组的容量,如果当前数组已使用长度+1后的大于当前的数组长度,则调用grow方法扩容(原来的1.5倍)
- 确保新增的数据有地方存储之后,则将新元素添加到位于size的位置上。
- 返回添加成功布尔值。
添加数据的流程如下
2.2 如何实现数组和List之间的转换
- 数组转List ,使用JDK中java.util.Arrays工具类的asList方法
- List转数组,使用List的toArray方法。无参toArray方法返回 Object数组,传入初始化长度的数组对象,返回该对象数组
List<String> myList = Arrays.asList(myArray); // 数组转list
str=myList.toArray(new String[0]); // list转数组
继续追问:用Arrays.asList转List后,如果修改了数组内容,list受影响吗?List用toArray转数组后,如果修改了List内容,数组受影响吗?
- Arrays.asList转换list之后,如果修改了数组的内容,list会受影响,因为它的底层使用的Arrays类中的一个内部类ArrayList来构造的集合,在这个集合的构造器中,把我们传入的这个集合进行了包装而已,最终指向的都是同一个内存地址
- list用了toArray转数组后,如果修改了list内容,数组不会影响,当调用了toArray以后,在底层是它是进行了数组的拷贝,跟原来的元素就没啥关系了,所以即使list修改了以后,数组也不受影响
2.3 ArrayList 与 LinkedList 区别?🔥
视频讲解:常见集合篇-08-ArrayList和LinkedList的区别是什么?_哔哩哔哩_bilibili
- 底层数据结构:
ArrayList
底层使用的是动态数组;LinkedList
底层使用的是 双向链表 - 插入和删除效率不同: ArrayList在尾部的插入和删除操作效率较高,但在中间或开头的插入和删除操作效率较低,需要移动元素。LinkedList在任意位置的插入和删除操作效率都比较高,因为只需要调整节点之间的指针。
- 随机访问的效率不同:ArrayList支持通过索引进行快速随机访问,时间复杂度为O(1)。LinkedList需要从头或尾开始遍历链表,时间复杂度为O(n)。
- 空间占用:
ArrayList
在创建时需要分配一段连续的内存空间,列表的结尾会预留一定的容量空间。而 LinkedList 的每个节点不光需要存储元素还要存储指针。 - 使用场景: ArrayList适用于频繁随机访问和尾部的插入删除操作,而LinkedList适用于频繁的中间插入删除操作和不需要随机访问的场景。
- 是否保证线程安全:
ArrayList
和LinkedList
都不是线程安全的;
2.4 把ArrayList变成线程安全有哪些方法?
- 使用Collections类的synchronizedList方法将ArrayList包装成线程安全的List:
- 使用CopyOnWriteArrayList类代替ArrayList,它是一个线程安全的List实现:
- 使用Vector类代替ArrayList,Vector是线程安全的List实现:
2.5 ArrayList list=new ArrayList(10)中的 list 扩容几次
在 ArrayList 的源码中提供了一个带参数的构造方法,这个参数就是指定的集合初始长度,所以给了一个10的参数,就是指定了集合的初始长度是10,这里面并没有扩容。
2.6 ArrayList 可以添加 null 值吗?
ArrayList
中可以存储任何类型的对象,包括 null
值。不过,不建议向 ArrayList
中添加 null
值, null
值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常。