- Java 中常用的容器有哪些?
这里java容器分为两大部分:Collection和Map。Collection是单个元素的集合,又可以分为List、Queue和Set。Map用来存储一组键值对,又可以分为HashMap和TreeMap。是引用
- ArrayList 和 LinkedList 的区别?
动态数组和链表的区别
- ArrayList 实现 RandomAccess 接口有何作用?为何 LinkedList 却没实现这个接口?
RandomAccess接口这个空架子的存在,是为了能够更好地判断集合是否ArrayList或者LinkedList,从而能够更好选择更优的遍历方式
实现RandomAccess接口的List集合采用一般的for循环遍历,而未实现这接口则采用迭代器。
ArrayList用for循环遍历比iterator迭代器遍历快,LinkedList用iterator迭代器遍历比for循环遍历快
- ArrayList 的扩容机制?
1.当我们要add进第一个元素到ArrayList时,elementData.length为0(因为还是一个空的list,里面还没有数据,所以没有进行扩容,默认扩容10),因为执行了ensureCapacityInternal()方法,所以minCapacity此时为10。此时,minCapacity - elemetData.length > 0(minCapacity=10,elemetData.length=0)成立,所以会进入==grow(minCapacity)==方法。
2.当add第2个元素时,minCapacity为2,此时elementData.length(容量)在添加第一个元素后扩容成10了。此时,minCapacity - elementData.length > 0不成立,所以不会进入(执行)==grow(minCapacity)方法。
添加第3、4…到第10个元素时,依然不会执行grow()==方法,数组容量都为10。
3.知道添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进行grow方法进行扩容
4.grow方法( 1.5倍)
具体看这篇博客我觉得写得不错:Java集合之ArrayList扩容机制
- Array 和 ArrayList 有何区别?什么时候更适合用 Array?
1.Array可以包含基本类型和对象类型,ArrayList只能包含对象类型;
2.Array(数组)的大小是固定的,ArrayList(列表)的大小是动态变化的;
3.ArrayList提供了更多的方法和特性:addAll()、removeAll()、iterator等;
4.对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
- HashMap 的实现原理/底层数据结构?JDK1.7 和 JDK1.8
HashMap的存储结构是数组+链表的结构
初始长度为16,加载因子是0.75
1.7与1.8之间 我的理解就是多了一个红黑树 还有一个就是定义的时候,1.8元素由Entry变为Node
插入元素的时候jdk1.7是在链表的头部插入,jdk1.8是在链表的尾部插入元素,当链表的长度超过TREEIFY_THRESHOLD(值为8)时,链表会转变为Tree树形结构,因为链表长度过长会导致查找效率下降,转为树形结构后,性能会有较大的提升
- HashMap 的 put 方法的执行过程?
1.7
- 判断当前数组是否需要初始化。
2.如果 key 为空,则 put 一个空值进去。
3.根据 key 计算出 hashcode。
4.根据计算出的 hashcode 定位出所在桶。
5.如果桶是一个链表则需要遍历判断里面的 hashcode、key 是否和传入 key 相等,如果相等则进行覆盖,并返回原来的值。
6.如果桶是空的,说明当前位置没有数据存入;新增一个 Entry 对象写入当前位置。
1.8
1.判断当前桶是否为空,空的就需要初始化(resize 中会判断是否进行初始化)。
2.根据当前 key 的 hashcode 定位到具体的桶中并判断是否为空,为空表明没有 Hash 冲突就直接在当前位置创建一个新桶即可。
3.如果当前桶有值( Hash 冲突),那么就要比较当前桶中的 key、key 的 hashcode 与写入的 key 是否相等,相等就赋值给 e,在第 8 步的时候会统一进行赋值及返回。
4.如果当前桶为红黑树,那就要按照红黑树的方式写入数据。
5.如果是个链表,就需要将当前的 key、value 封装成一个新节点写入到当前桶的后面(形成链表)。
6.接着判断当前链表的大小是否大于预设的阈值,大于时就要转换为红黑树。
7.如果在遍历过程中找到 key 相同时直接退出遍历。
8.如果 e != null 就相当于存在相同的 key,那就需要将值覆盖。
9.最后判断是否需要进行扩容。
- HashMap 的 get 方法的执行过程?
1.7
1.首先也是根据 key 计算出 hashcode,然后定位到具体的桶中。
2.判断该位置是否为链表。
3.不是链表就根据 key、key 的 hashcode 是否相等来返回值。
4.为链表则需要遍历直到 key 及 hashcode 相等时候就返回值。
5.啥都没取到就直接返回 null 。
1.8
1.首先将 key hash 之后取得所定位的桶。
2.如果桶为空则直接返回 null 。
3.否则判断桶的第一个位置(有可能是链表、红黑树)的 key 是否为查询的 key,是就直接返回 value。
4.如果第一个不匹配,则判断它的下一个是红黑树还是链表。
5.红黑树就按照树的查找方式返回值。
6.不然就按照链表的方式遍历匹配返回值。
HashMap底层原理剖析及JDK1.7和1.8的对比
- HashMap 的 resize 方法的执行过程?
原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。
resize()是HashMap中扩容的方法,当HashMap中存的数据量大于threshold时或进行初始化HashMap的时候会进行扩容的操作,即执行resize()方法进行扩容
1 在HashMap的putVal()方法中,会先判断table是否为空,如果为空的话,会执行resize(),然后就初始化table
2 在HashMap中的存储的数据量大于threshold时,会执行resize()方法
数组扩容的过程中都是扩容的是原来长度的两倍
扩容过程中,会先创建一个长度为原来两倍新的table数组,同时会旧table中链表进行重哈希,在这个过程中,会根据hash后得到的不同结果,形成两条链表,一条是放在数组下标与原来相同的下面,另一条将放在原来的{旧数组下标值+扩容的长度}下面,这样就能够把HashMap中的链表长度变短了。
我的理解只能到这一步了
- HashMap 的 size 为什么必须是 2 的整数次方?
1.提升计算效率,更快算出元素的位置
位运算永远比取余运算快得多,在length为2的整数次方的情况下,hash(key)%length能被替换成高效的hash(key)&(length-1),两者的结果是相等的。
2.减少哈希碰撞,使得元素分布均匀
如果数组长度是2的整数次方时,也就是一个偶数,length-1就是一个奇数,奇数的二进制最后一位是1,因此不管hash(key)是多少,hash(key)&(length-1)的二进制最后一位可能是0,也可能是1,取决于hash(key)。即如果hash(key)是奇数的话,则映射到数组上第奇数个位置上。
如果length是一个奇数的话,length-1就是一个偶数,偶数的二进制最后一位是0,则不管hash(key)是奇数还是偶数,该元素都会被映射到数组上第偶数个位置上,奇数位置上没有任何元素!
【HashMap】为什么长度总是2的整数次方
- HashMap 多线程死循环问题?
多线程put操作后,get操作导致死循环。
这个我也看不明白 大家参考一下这个博主
多线程下的HashMap死循环问题详解
- HashMap 的 get 方法能否判断某个元素是否在 map 中?
HashMap 的 get 函数的返回值不能判断一个 key 是否包含在 map 中,因为 get 返回 null 有可能是不包含该 key,也有可能该 key 对应的 value 为 null。因为 HashMap 中允许 key 为 null,也允许 value 为 null。
- HashMap 与 HashTable 的区别是什么?
写到这儿 我发现又有人写过这些问题的回答了
大家直接看她们的吧
Java集合类常见面试题
-
HashMap 与 ConcurrentHashMap 的区别是什么?
-
HashTable 和 ConcurrentHashMap 的区别?
-
ConcurrentHashMap 的实现原理是什么?
-
HashSet 的实现原理?
-
HashSet 怎么保证元素不重复的?
-
LinkedHashMap 的实现原理?
-
Iterator 怎么使用?有什么特点?
-
Iterator 和 ListIterator 有什么区别?
-
Iterator 和 Enumeration 接口的区别?
-
fail-fast 与 fail-safe 有什么区别?
-
Collection 和 Collections 有什么区别?