Java容器、HashMap原理、HashMap死循环、ConcurrentHashMap、ArrayList和LinkedList 、迭代器、常用集合默认长度及扩容机制、面试题

Java容器

  1. Java 容器都有哪些?

Java 容器分为 Collection 和 Map 两大类,其下又有很多子类

Collection:

ListArrayListLinkedListVectorStack

SetHashSetLinkedHashSetTreeSet

Map:HashMapLinkedHashMapTreeMapConcurrentHashMapHashtable

  1. Collection 和 Collections 有什么区别?

Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。

Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法: Collections. sort(list)。

3.List、Set、Map 之间的区别是什么?

list集合是有序集合,set是无序集合

Set和map不可重复

4.HashMap 和 Hashtable 有什么区别?

HashMap是非线程安全的,HashTable是线程安全的。

HashMap的键和值都允许有null值存在,而HashTable则不行。否则会抛出空指针异常

因为线程安全的问题,HashMap效率比HashTable的要高。

Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。

一般现在不建议用HashTable,

①是HashTable是遗留类,内部实现很多没优化和冗余。

②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。

  1. 如何决定使用 HashMap 还是 TreeMap?

对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。

6.说一下 HashMap 的实现原理?

HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。

hash()算法 不等同于 hashCode() 方法,而是= key.hashCode() ^ (h >>> 16),h >>> 16右移后可以发现,h 的高16位特征转移到低16位,然后再进行异或运算,hash = h ^ (h >>> 16) ,在上述例子中发现这样得到的 hash 值高16位特征依旧不变,低16位是原来高低位的混合特征。这样进行数组槽位运算时,高位特征不会被槽位数 n 的二进制码屏蔽。

因为计算二进制与运算时,只有相同位同时为1时,与运算结果才为1,此时 n-1 = 15, 高位为0,与运算 (n - 1) & hash 的结果高位一定是0, 就是hash的高位就会被屏蔽。 如上所说,进行右移异或运算后,key.hashCode() 高位与低位的特征会被混合保留起来,即使进行槽位运算也不至于丢失。这样可以减少hash碰撞,提高性能

7.说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。

  1. Array List 和 LinkedList 的区别是什么?

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。

随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。

增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

  1. 如何实现数组和 List 之间的转换?

数组转 List:使用 Arrays. asList(array) 进行转换。

List 转数组:使用 List 自带的 toArray() 方法。

  1. Array List 和 Vector 的区别是什么?

线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。

性能:ArrayList 在性能方面要优于 Vector。

扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

11.Array 和 ArrayList 有何区别

Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。

Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。

Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

HashMap & ConcurrentHashMap的区别?

答:在多线程环境下,使用HashMap进行put操作会引起死循环(因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry),导致CPU利用率接近100%,所以在并发情况下不能使用HashMap

HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。

ConcurrentHashMap和Hashtable的区别?

ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势。HashMap 没有考虑同步,HashTable 考虑了同步的问题。但是 HashTable 在每次同步执行时都要锁住整个结构。效率低下, ConcurrentHashMap 锁的方式是稍微细粒度的。

ConcurrentHashMap实现原理

ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

JDK1.7 : 【数组(Segment) + 数组(HashEntry) + 链表(HashEntry节点)】

采用分段锁(Segment 数组,一个 Segment 就是一个锁)技术,每当一个线程访问 HashEntry 中存储的数据从而占用一个 Segment 锁时,并不会影响到其他的 Segment,也就是说,如果 Segment 数组中有 10 个 元素,那理论上是可以允许 10 个线程同时执行的。 put 操作:首先,既然 ConcurrentHashMap 使用分段锁 Segment 来保护不同段的数据,那么在插入和获取元素的时候,必须先通过 Hash 算法定位到 Segment,然后在对应的 Segment 中进行真正的 put。   

JDK1.8 : Node数组+链表 / 红黑树

直接抛弃了 Segment 锁,一个 ConcurrentHashMap 包含一个 Node 数组,synchronized + CAS 保证并发安全性,Node 数组其实就是一个哈希桶数组,每个 Node 头节点及其所有的 next 节点组成的链表就是一个桶,只要锁住这个桶的头结点,就不会影响其他哈希桶数组元素的读写 。 put 方法:遍历 table 数组,根据 hashcode 定位 Node:如果 Node 为空表示当前位置可以写入数据,利用 CAS 尝试写入(失败则自旋)

如果当前位置的 hashcode == MOVED == -1,则需要对 Node 数组进行扩容

如果 Node 不为空并且也不需要进行扩容,则利用 synchronized 锁写入数据

红黑树

红黑树是一种自平衡二叉查找树,是一种数据结构,可以在O(logn)时间内做查找,插入和删除,这里的n是树的结点个数。

红黑树的特性:

·每个结点是黑色或者红色。

·根结点是黑色。

·每个叶子结点(NIL)是黑色。 [注意:这里叶子结点,是指为空(NIL或NULL)的叶子结点!]

·如果一个结点是红色的,则它的子结点必须是黑色的。

·每个结点到叶子结点NIL所经过的黑色结点的个数一样的。[确保没有一条路径会比其他路径长出俩倍,所以红黑树是相对接近平衡的二叉树的!]

12.在 Queue 中 poll()和 remove()有什么区别?

相同点:都是返回第一个元素,并在队列中删除返回的对象。

不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。

13.什么是迭代器(Iterator)?

Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获

14.Iterator 怎么使用?有什么特点?

List list = new ArrayList<>();

Iterator it = list. iterator();

while(it. hasNext()){

String obj = it. next();

System. out. println(obj);

特点:更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出ConcurrentModificationException 异常

15. Iterator 和 ListIterator 有什么区别?

Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。

Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。

java集合的默认长度

ArrayList默认初始容量为10

底层数据结构是数组结构

扩容增量:原容量的 0.5倍+1

如 ArrayList的容量为10,一次扩容后是容量为16

Vector默认初始容量为10

底层数据结构是数组结构

加载因子为1:即当 元素个数 超过 容量长度 时,进行扩容

如 Vector的容量为10,一次扩容后是容量为20

HashSet默认初始容量为16

底层实现是一个HashMap(保存数据),实现Set接口

加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容

扩容增量:原容量的 1 倍

如 HashSet的容量为16,一次扩容后是容量为32

HashMap默认初始容量为16,长度始终保持2的n次方

加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容

扩容增量:原容量的 1 倍

如 HashMap的容量为16,一次扩容后是容量为32

HashTable:默认初始容量为11

线程安全,但是速度慢,不允许key/value为null

加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容

扩容增量:2*原数组长度+1

如 HashTable的容量为11,一次扩容后是容量为23

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值