集合
-
- 1.HashMap底层?扩容机制?1.7-1.8的升级?
- 2.HashMap的长度为什么是2的幂次方?
- 3.HashMap 插入1.7和1.8的区别?
- 4.什么是红黑树?O(logn)
- 5.HashMap为什么会使用红黑树?
- 6.ArrayList底层?扩容机制?
- 7.LinkedList底层?扩容机制?
- 8.ArrayList可以序列化,但是为什么不直接序列化?
- 9.数组和ArrayList的区别,有数组为啥还有ArrayList?
- 10.ConcurrentHashMap?
- 11.HashMap的put的理解?
- 12.HashMap的get的理解?
- 13.HashSet的底层?
- 14.HashSet是如何保证数据不重复的?
- 15.TreeMap?HashMap?HashTable?LinkedHashMap?区别?
- 16.HashSet?LinkedHashSet?TreeSet?区别?
- 17.什么是Hash?什么是Hash冲突?怎么解决的?
- 18.什么是扰动函数?
- 19.集合中的快速失败机制"fail-fast"和安全失败?
- 20.怎么确保一个集合不能被修改?
- 21.Iterator和ListIterator区别?
- 22.移除collection元素?
- 23.遍历list的不同方式?
- 24.ArrayList为什么不直接序列化其内部数组?
- 25.快排时间复杂度?
- 26.什么叫序列化?
- 27.==和equals的区别?
- 28.1<<4是什么意思?
- 29.时间复杂度的排序?
- 30.HashMap中,string、Integer这样的包装类适合作为key么?任何类都可以作为key么?Object作为HashMap的Key,应该怎么做?
- 31.Collection和Collections的区别?
- 32.Collections.sort的原理?
- 33.怎么确保一个集合不被修改?
- 34.哪些集合类是线程安全的?
持续更新中~
1.HashMap底层?扩容机制?1.7-1.8的升级?
- 底层数据结构:
- 1.7:是数组+链表;
- 1.8:底层是数组+链表+红黑树;
- 默认大小16;
- 怎么扩容;
- 创建一个新Entry空数组,是原先的两倍;
- 遍历原有的数组,把之前的数组重新Hash到新数组中;
- 什么时候扩容:负载因子是0.75f,比如100,数量到76的时候就扩容;
2.HashMap的长度为什么是2的幂次方?
为了能让HashMap存取高效,尽量减少碰撞,就是要尽量把数据分配均匀,每个链表/红黑树的长度大致相同。
3.HashMap 插入1.7和1.8的区别?
在 Java 1.7 中,HashMap 使用了数组和链表来存储键值对。插入操作大致分为以下几个步骤:
- 计算哈希值:首先,使用键的
hashCode()
方法计算其哈希值。 - 定位桶:通过哈希值与数组长度减 1 进行位与操作,确定键应该放入哪个桶中。
- 处理哈希冲突:如果桶中已经有元素,就遍历链表,查找是否有相同的键。
- 如果有相同的键,则更新对应的值。
- 如果没有相同的键,则在链表末尾添加新的键值对。
- 扩容:如果链表长度大于阈值(数组长度的 0.75 倍),则对 HashMap 进行扩容,并重新计算所有键的哈希值。
在 Java 1.8 中,HashMap 对插入过程进行了优化,引入了红黑树来处理链表过长的情况。插入操作的大致步骤如下:
- 计算哈希值:同样,使用键的
hashCode()
方法计算其哈希值。 - 定位桶:通过哈希值与数组长度减 1 进行位与操作,确定键应该放入哪个桶中。
- 处理哈希冲突:
- 如果桶中已经有元素,并且元素是链表,则遍历链表,查找是否有相同的键。
- 如果有相同的键,则更新对应的值。
- 如果没有相同的键,则在链表末尾添加新的键值对。
- 如果链表长度大于 8,并且数组长度大于 64,则将链表转换为红黑树。
- 如果桶中已经有元素,并且元素是红黑树,则在红黑树中插入新的键值对。
- 如果桶中已经有元素,并且元素是链表,则遍历链表,查找是否有相同的键。
- 扩容:如果 HashMap 的大小超过了阈值(数组长度的 0.75 倍),则对 HashMap 进行扩容,并重新计算所有键的哈希值。
从 Java 1.7 到 Java 1.8,HashMap 的插入过程在处理哈希冲突时有所不同。Java 1.7 使用链表来处理冲突,而Java 1.8 引入了红黑树来优化链表过长的情况。这种优化可以减少查找时间,提高 HashMap 的性能。
4.什么是红黑树?O(logn)
- 红黑树是一种不严格平衡二叉树,不追求绝对的平衡,允许局部不平衡;
- 根节点是黑色;
- 叶子节点是黑色的时候是空节点,叶子节点不存数据;
- 相邻节点不能同时为红色,红黑被隔开;
- 每个节点到达其可达叶子节点的所有路径,含相同数目的黑色节点。
5.HashMap为什么会使用红黑树?
java8不是用红黑树来管理hashmap,而是在hash值相同的情况下(且重复数量大于8),用红黑树来管理数据。 红黑树相当于排序数据,可以自动的使用二分法进行定位,性能较高。一般情况下,hash值做的比较好的话基本上用不到红黑树。
红黑树牺牲了一些查找性能 但其本身并不是完全平衡的二叉树。因此插入删除操作效率略高于AVL树。
红黑树相比avl树,在检索的时候效率其实差不多,都是通过平衡来二分查找。但对于插入删除等操作效率提高很多。红黑树不像avl树一样追求绝对的平衡,他允许局部很少的不完全平衡,这样对于效率影响不大,但省去了很多没有必要的调平衡操作,avl树调平衡有时候代价较大,所以效率不如红黑树。
6.ArrayList底层?扩容机制?
-
底层是数组实现的,是一种随机访问模式,实现了RandomAccess接口,所以查询特别快;
-
线程不安全的;
-
查询快,删除更新慢;
-
默认长度是10,也可以指定长度;
-
扩容方式:
- 重新定义n+n/2长度的数组;
- 然后把原数组数据原封不动复制到新数组中;
- 再把原数组的地址换到新数组里;
-
新增:
- 先做一个长度判断,长度不够是需要扩容的,采用位运算,右移一位;
- 在第n位增加一个数,n后所有的元素放在n+1的位置;
-
ArrayList不会初始化数组大小;
-
插入删除一定会慢么?
- 取决于离数组末端有多远;
-
怎样实现删除?
- 删除第n个位置,就是复制n+1后的元素,放到n的位置,就是覆盖了。
-
ArrayList不适合做队列,队列是先入先出,涉及到数据迁移耗费性能;
7.LinkedList底层?扩容机制?
- 底层是双向链表(1.6之前是循环链表);
- 线程不安全的;
- 插入删除快,查询慢;
- 不存在扩容问题,会在需要的时候动态扩容;
- 链表需要内存比数组多;
双向链表是指每个数据节点都有两个指针,分别指向直接后继和直接前驱所以双向链表中的任意一个节点都可以很方便的访问它的前后。
8.ArrayList可以序列化,但是为什么不直接序列化?
出于效率考虑,长度100,但是实际只用50,剩下50不用序列化,提高效率节省空间。
9.数组和ArrayList的区别,有数组为啥还有ArrayList?
数组是一种固定长度的数据结构,可以存储相同类型的元素,并通过索引访问和修改元素。数组的主要优点是在访问元素时具有较高的性能,因为可以通过索引直接定位元素。然而,数组也有一些限制。首先,数组长度固定,无法动态调整大小,这意味着一旦数组创建后,无法添加或删除元素。其次,数组不提供内置的方法来执行常见的操作,如添加、删除、搜索等,需要手动编写代码来处理这些操作。
ArrayList是Java集合框架中的一个类,它是基于数组实现的动态数组。与数组不同,ArrayList的长度是可变的,可以根据需要动态调整大小。ArrayList提供了一组方便的方法来添加、删除、搜索和访问元素,使操作更加简单和灵活。此外,ArrayList还提供了各种实用的方法,如排序、查找、替换等,使数据操作更加方便。另一个重要的区别是数组可以存储基本类型和对象类型,而ArrayList只能存储对象类型。如果需要存储基本类型,可以使用对应的包装类来存储在ArrayList中。
10.ConcurrentHashMap?
ConcurrentHashMap是Java并发包java.util.concurrent中的一个类,它提供了线程安全的HashMap实现。ConcurrentHashMap的底层实现相当复杂,主要是为了提供高并发、高性能的线程安全Map。
底层:
- 1.7-ReentrantLock+Segment+HashEntry(1.7的时候没有红黑树)
- 1.8-HashEntry+链表+红黑树+synchronized+cas
HashEntry最小的容量为2
Segment的初始化容量是16;
分段锁(Segmentation)
在 Java 7 中,Conc