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 的值),即 & 运算后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性。