JDK1.8中HashMap在出现hash碰撞时链表长度超过8一定会变成红黑树?
首先答案是:否,因为实际上转换红黑树有个大前提,就是当前hash table的长度也就是HashMap的capacity(不是size)不能小于64.小于64就只是做个扩容。
HashSet和TreeSet的区别
类 | 底层数据结构 | 顺序 | 存放null值 |
---|---|---|---|
HashSet | 哈希表 | 无序 | 能够存放一个null值 |
TreeSet | 二叉树 | 有序;分为自然排序和定制排序 | 不能够存放null值 |
HashSet的底层原理
HashSet的底层就是在HashMap的基础上包装了一层,只不过存储的时候的value是默认存储了一个Object的静态常量,存取都是使用key。
LinkedHashMap底层原理
LinkedHashMap通过维护一个运行于所有条目的双向链表,保证了集合元素迭代的顺序,这个顺序可以是插入顺序或者访问顺序。
LinkedHashMap可以看作是HashMap和LinkedList的集合,它使用HashMap来存储数据,使用LinkedList来维护顺序。
为什么集合类没有实现Cloneable和Serializable接口
克隆(cloning)或者序列化(serialization)的语义和含义是跟具体的实现相关的。因此应该由集合类的具体实现类来决定如何被克隆或者序列化
Iterator和ListInterator的区别
ListIterator 继承 Iterator
ListIterator 比 Iterator多方法
- 使用范围不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子类
- ListIterator 有 add 方法,可以向 List 中添加对象;Iterator 不能
- ListIterator 有 hasPrevious() 和 previous() 方法,可以实现逆向遍历;Iterator不可以
- ListIterator 有 nextIndex() 和previousIndex() 方法,可定位当前索引的位置;Iterator不可以
- ListIterator 有 set()方法,可以实现对 List 的修改;Iterator 仅能遍历,不能修改
Collection和Collections的区别
- java.util.Collection是一个顶层集合接口,它定义了对集合对象的基本操作的通用方法
- java.util.Collections是一个工具类,它提供了一系列静态方法,用于对集合进行排序、搜索、创建线程集合等操作
如何实现数组和 List 之间的转换?
数组转List使用:Arrays.asList()。
List转数组使用:list.toArray()和list.toArray(数组类型),例如:list.toArray(new String[0])
ArrayList 和 Vector 的区别是什么?
Vector使用了Synchronized实现了线程安全,但效率就变得很慢,同时Vector在每扩容的时候会每次增加一倍。
ArrayList是线程不安全的,但效率就会比Vector快的多,同时ArrayList每次增加50%。
Array 和 ArrayList 有何区别?
Array最高效但容量初始化时就固定了且无法动态改变。
ArrayList容量可以动态增加,但牺牲了效率,同时不能够存放基本类型。
Queue中的方法们不同点是啥?
offer()和add()
两个方法都是再往队列里面添加一个元素、但在队列已经满了的时候,调用 add方法就会抛出一个unchecked异常,调用offer方法就返回一个false
peek()和element()
两个方法都是在不移除的情况下返回当前头部的元素、但在队列为空的情况下,调用 element方法就会抛出一个NoSuchElementException异常、调用peek方法就返回null
poll()和remove()
两个方法都是移除当前的头部元素、但是在队列为空的情况下,调用remove()方法就会抛出一个NoSuchElementException异常、调用poll()方法返回null
怎么确保一个集合不能被修改?
调用Collections.unmodifiablexxx()方法。
数组的特点
- 有序性:数组中的元素是有序的,通过下标来访问。
- 不可变性:数组一旦初始化,则长度(数组中元素的个数)不可变。 数组是一种连续存储线性结构,元素类型相同,大小相等。
Java中常见的集合
- 顶层父接口:Map接口、Collection接口
- Collection接口的子接口:List接口、Set接口
- Map接口的实现类:HashMap、TreeMap、Hashtable、ConcurrentHashMap、LinkedHashMap
- Set接口的实现类:HashSet、TreeSet、LinkedHashSet
- List接口的实现类:ArrayList、LinkedList、Stack、Vector
简单聊聊 Set、List、Map的区别
List接口
- 可以允许重复的对象。
- 可以插入多个null元素。
- 是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
Set接口
- 不允许重复对象
- 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
- 只允许一个 null 元素
Map接口
- 键值对
- 可以有多个NULL值,但只能有一个NULL键
使用场景
如果经常使用下标来访问集合的话且需要有序集合,就使用List接口。如果查询多、增删少就用ArrayList,如果查询少、增删多就用LinkedList。
如果需要保证元素的唯一性,就使用Set接口。如果不需要排序就使用HashSet、不然可以使用TreeSet、LinkedHashSet等。
如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
为什么ArrayList的elementData是用transient修饰的
首先要知道被
transient
修饰的变量 不参与序列化的过程。
ArrayList
实现了Serializable
接口,这意味着ArrayList
接口是可以被序列化的。但在序列化的时候elementData
不一定是满的,例如elementData
创建了10大小的数组但只使用3个,所以为了节省空间,没必要序列化整个数组,因此ArrayList
使用transient
修饰了elementData
,重写了writeObject
方法。
HashSet如何检测对象是否重复
当你把对象加入
HashSet
时,HashSet
会先计算对象的hashcode
值来判断对象加入的位置,同时也会与其他加入的对象的hashcode
值作比较,如果没有相符的hashcode
,HashSet
会假设对象没有重复出现。但是如果发现有相同hashcode
值的对象,这时会调用equals()
方法来检查hashcode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让加入操作成功。
HashMap的底层实现
JDK1.8之前
JDK1.8 之前
HashMap
底层是数组
和链表
结合在一起使用也就是链表散列
。HashMap
通过key
的hashCode
经过扰动函数处理过后得到hash
值,然后通过(n - 1) & hash
判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的hash
值以及key
是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。所谓扰动函数指的就是
HashMap
的hash
方法。使用hash
方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。
JDK1.8之后
JDK1.8大部分都与之前一样,只是在解决
哈希冲突
的地方有了比较大的变化。当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。ps:红黑树是一个自平衡的二叉查找树.
HashSet,TreeSet和LinkedHashSet三者的异同之处
- 如果需要一个访问快速的Set,就用HashSet
- 如果需要一个排序的Set,就用TreeSet
- 如果需要记录插入时的顺序,就用LinkedHashSet
HashMap长度为什么是2的幂次方
为了加快哈希计算以及减少哈希冲突
“取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。
length 为偶数时,length-1 为奇数,奇数的二进制最后一位是 1,这样便保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(这取决于 h 的值),即 & 运算后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性。