
目录
4. ArrayList、LinkedList 、Vector区别?
19.为什么 HashMap 链表转红黑树的阈值为 8 呢?
1.数组存储的特点?
- 1.数组一旦初始化,其长度就是确定的。
- 2.数组中的多个元素依次紧密排列,有序,可重复
- 3.元素的类型既可以是基本数据类型,也可以是引用数据类型
2.常见的集合有哪些?
- 集合类主要由两个接口:Collection和Map派生出来的
- Collection有三个子接口:List、Set、Queue。
- collection:存储一个一个的数据
- lsit:存储有序的、可重复的数据
- 实现类: ArrayList、LinkedList 、Vector
- set:存储无序的、不可重复的数据
- 实现类:HashSet、LinkedHashSet、TreeSet
- queue:一个用于保持元素队列的集合。
- 实现类:PriorityQueue、ArrayDeque
- Map:存储一对一对的数据
- 实现类: HashMap、LinkedHashMap、TreeMap、Hashtable、 Properties
3.说一下Iterator迭代器?
- 作用:遍历集合的利器
Iterator iterator = list.iterator(); //hasNext():判断是否还有下一个元素 while (iterator.hasNext()){ //next():①指针下移②将下移以后集合位置上的元素返回 System.out.println(iterator.next()); }
4. ArrayList、LinkedList 、Vector区别?
- ArrayList
- list的主要实现类,线程不安全的、效率高,底层使用Object[]数组存储.
- 在添加数据、查找数据时,效率较高;在插入、删除数据时,效率较低
- 可以通过下标直接获取元素
- 当添加第11个元素时扩容,默认扩容1.5倍
- JDK1.7初始化数组大小为10,JDK1.8首次添加元素时,才会初始化数组大小为10
- LinkedList
- 线程不安全的,底层使用双向链表的方式进行存储
- 在插入、删除数据时,效率较高;在添加数据、查找数据时,效率较低;
- 不可以通过下标直接获取元素
- 不考虑扩容
- Vector
- list的古老实现类,线程安全的、效率低,底层使用Object[]数组存储
- 当添加第11个元素时扩容,默认扩容2倍
- JDK1.8初始化数组大小为10
5. 简单说一下set?
set:存储无序的、不可重复的数据
主要实现类:HashSet、LinkedHashSet、TreeSet
- HashSet:底层使用的是HashMap,即使用数组+单向链表+红黑树结构进行存储。
- LinkedHashSet:是HashSet的子类,在现有的数组+单向链表+红黑树结构的基础上,又添加了一组双向链表,用于记录添加元素的先后顺序
- TreeSet :底层使用红黑树存储。可以按照添加的元素的指定的属性的大小顺序进行遍历。
6.简单说一下map?
主要实现类 :HashMap、LinkedHashMap、TreeMap、Hashtable、 Properties
- HashMap:主要实现类;线程不安全的,效率高;可以添加null的key和value值;jdk1.8底层使用数组+单向链表+红黑树结构,jdk1.7底层使用数组+单向链表
- TreeMap:古老实现类;线程安全的,效率低;不可以添加null的key或value值;底层使用数组+单向链表
- LinkedHashMap:是HashMap的子类;在HashMap使用的数据结构的基础上,增加了一对双向链表,用于记录添加的元素的先后顺序,遍历元素时,就可以按照添加的顺序显示。对于频繁的遍历操作,建议使用此类。
- Hashtable:古老实现类;线程安全的,效率低;不可以添加null的key或value值;底层使用数组+单向链表结构存储
- Properties:其key和value都是String类型。
7.ArrayList 的扩容机制了解吗?
- ArrayList 是基于数组的集合,数组的容量是在定义的时候确定的,如果数组满了,再插入,就会数组溢出。所以在插入时候,会先检查是否需要扩容,如果当前容量+1 超过数组长度,就会进行扩容。
- ArrayList 的扩容是创建一个1.5 倍的新数组,然后把原数组的值拷贝过去。
8.HashMap中元素的特点?
- 1.HashMap中的所有的key彼此之间是不可重复的、无序的。
- 2.HashMap中的所有的value彼此之间是可重复的、无序的。
- 3.HashMap中的一个key-value,就构成了一个entry。
- 4.HashMap中的所有的entry彼此之间是不可重复的、无序的。所有的entry就构成了一个Set集合。
9. HashMap 的添加/修改的过程?
- jdk1.7:将(key1,value1)添加到map中:首先初始化长度16的数组
- 1.调用key1所在类的hash()方法,计算key1对应的哈希值1,此哈希值1经过hash()算法之后,得到哈希值2,哈希值2再经过(idexFor()算法之后,就确定了(key1,value1)在数组table中的索引位置i。
- 1.1.如果此索引位置i的数组上没有元素,则(key1,value1)添加成功。将(key1,value1)存放到数组的索引i的位置
- 1.2.如果此索引位置i的数组上有元素(kev2.value2),则需要继续比较key1和key2的哈希值2,此过程称为哈希冲突
- 2.1.如果key1的哈希值2与key2的哈希值2不相同,则(key1,value1)添加成功。
- 2.2.如果key1的哈希值2与key2的哈希值2相同,则需要继续比较key1和key2的equals()。要调用key1所在类的equals(),将key2作为参数传递进去。
- 3.1调用equals(),返回false :则(key1.value1)添加成功。
- 3.2调用equals(),返回true:则认为key1和key2是相同的。默认情况下, value1替换原有的value2.
- JDK1.8中与JDK1.7的区别:
- JDK.18中当我们创建了HashMap实例以后,底层并没有初始化table数组。
- JDK.18中当首次添加(key,value)时,进行判断,如果发现table尚未初始化,则对数组进行初始化,Node类型的数组
- 在JDK1.7中采用头插法,在JDK1.8中采用尾插法
- 在JDK1.7中采用数组+单向链表,在JDK1.8中采用数组+单向链表+红黑树
10.hashMap的扩容机制?
- 当元素的个数达到临界值(->数组的长度*加载因子)时,就考虑扩容。
- 默认扩容为原来的2倍。
11.什么时候扩容?
- HashMap 会在存储的键值对数量超过阈值(即容量 * 加载因子)时进行扩容。
- 加载因子默认是0.75
加载因子为什么默认是0.75 ?
- Node[] table的初始化长度length为16,默认的loadFactor是0.75,0.75是对空间和时间效率的一个平衡选择,根据泊松分布,loadFactor 取0.75碰撞最小
如果内存空间很多而又对时间效率要求很高,可以降低负载因子Load factor的值 。
如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadFactor的值,这个值可以大于1。
12.HashMap 的长度为什么是 2 的幂次方?
- 将HashMap的长度定为2 的幂次方,这样就可以使用(n -1) & hash位运算代替%取余的操作,提高性能。
- 数组的长度必须是 2 的整数次幂,这样可以保证
hash & (n-1)的结果能均匀地分布在数组中。
&操作的结果就是哈希值的高位全部归零,只保留 n 个低位,用来做数组下标访问。先判断是否小于16,小于的话,容量就会乘以2
int capacity = 1; while (capacity < initialCapacity)capacity <<= 1;
13.为什么建议设置HashMap的容量?
- HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容条件就是当HashMap中的元素个数超过临界值时就会自动扩容(threshold = loadFactor * capacity)。
- 如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容。
- HashMap每次扩容都需要重建hash表,非常影响性能。所以建议开发者在创建HashMap的时候指定初始化容量。
14.能说一下 HashMap 的底层数据结构吗?
- JDK 8 中 HashMap的数据结构是
数组+链表+红黑树。- JDK 7中 HashMap的数据结构是
数组+链表。
15.什么时候会将链表转换为红黑树?
- 如果数组索引i位置上的元素的个数达到8,并且数组的长度达到64时,我们就将此索引i位置上的多个元素改为使用红黑树的结构进行存储。
- 好处:
- 红黑树进行put()/get()/remove()操作的时间复杂度为O(logn),比单向链表的时间复杂度O(n)好,性能更高。
16.什么时候会将红黑树转换为链表?
- 当使用红黑树的索引i位置上的元素的个数低于6的时候,就会将红黑树结构退化为单向链表。
17.HashMap 的 hash 函数是怎么设计的?
static final int hash(Object key) { int h; //key 的hashCode 和key 的hashCode 右移16位做异或运算 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
- HashMap 的哈希函数是先拿到 key 的 hashcode,是一个 32 位的 int 类型的数值,然后让 hashcode 的高 16 位和低 16 位进行异或操作。有效降低哈希冲突
18.解决哈希冲突有哪些方法呢?
- 1.再哈希法 :准备两套哈希算法,当发生哈希冲突的时候,使用另外一种哈希算法,直到找到空槽为止。对哈希算法的设计要求比较高。
- 2.开放地址法:遇到哈希冲突的时候,就去寻找下一个空的槽
- 3.链地址法:当发生哈希冲突的时候,使用链表将冲突的元素串起来。(HashMap采用)
19.为什么 HashMap 链表转红黑树的阈值为 8 呢?
- 红黑树节点的大小大概是普通节点大小的两倍,所以转红黑树,牺牲了空间换时间,更多的是一种兜底的策略,保证极端情况下的查找效率。
阈值为什么要选 8 呢?和统计学有关。理想情况下,使用随机哈希码,链表里的节点符合泊松分布,出现节点个数的概率是递减的,节点个数为 8 的情况,发生概率仅为
0.00000006。至于红黑树转回链表的阈值为什么是 6,而不是 8?是因为如果这个阈值也设置成 8,假如发生碰撞,节点增减刚好在 8 附近,会发生链表和红黑树的不断转换,导致资源浪费
20.一般用什么作为HashMap的key?
一般用Integer、 String这种不可变类当HashMap当key, String类比较常用。
- String是不可变的,在它创建的时候 hashcode“就被缓存了,不需要重新计算。这就是HashMap中的key经常使用字符串的原因。
- 获取对象的时候要用到 equals()hashCode()以及equals()和hashCode() 方法,而Integer、 String这些类都已经重写了方法,不需要自己去重写这两个方法。
21.HashMap为什么线程不安全?
- 多线程下扩容死循环。JDK1.7中的HashMap使用头插法插入元素,在多线程的环境下,扩容的时候有可能导致环形链表的出现,形成死循环。
- 在JDK1.8中,在多线程环境下,会发生数据覆盖的情况。
22.HashMap和HashTable的区别?
HashMap和Hashtable都实现了Map接口。
- 1.HashMap可以接受为null的key和value, key为null的键值对放在下标为0的头结点的链表中,而Hashtable则不行。
- 2.HashMap是非线程安全的, HashTable是线程安全的。Jdk1.5提供了ConcurrentHashMap,它是HashTable的替代。
- 3.哈希值的使用不同, HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
23.TreeMap 和 HashMap 的区别?
- 1.HashMap
- 是基于数组+链表+红黑树实现的
- put 元素的时候会先计算 key 的哈希值,然后通过哈希值计算出数组的索引,然后将元素插入到数组中,如果发生哈希冲突,会使用链表来解决,如果链表长度大于 8,会转换为红黑树。
- get 元素的时候同样会先计算 key 的哈希值,然后通过哈希值计算出数组的索引,如果遇到链表或者红黑树,会通过 key 的 equals 方法来判断是否是要找的元素。
- 在没有发生哈希冲突的情况下,HashMap 的查找效率是 O(1)
- 2.TreeMap
- 是基于红黑树实现的
- put 元素的时候会先判断根节点是否为空,如果为空,直接插入到根节点,如果不为空,会通过 key 的比较器来判断元素应该插入到左子树还是右子树。
- get 元素的时候会通过 key 的比较器来判断元素的位置,然后递归查找。
- TreeMap 是基于红黑树实现的,所以 TreeMap 的查找效率是 O(logn)。
24.LinkedHashMap底层原理?
- LinkedHashMap是HashMap的子类。
- LinkedHashMap在HashMap使用的数组+单向链表+红黑树的基础上,又增加了一对双向链表,记录添加的(key,value)的先后顺序。便于我们遍历所有的key-value
25.讲讲 HashSet 的底层实现?
- HashSet 基于 HashMap 实现。放入HashSet中的元素实际上由HashMap的key来保存,而HashMap的value则存储了一个静态的Object对象。

2679

被折叠的 条评论
为什么被折叠?



