1. List,Set,Map区别
答:首先Map和Collection是并列的上层接口,List和Set是Collection的子接口。
- List(解决顺序问题):存储一组有序可重复的对象,实现类有ArrayList、LinkedList和Stack等。
- Set(元素独一无二):不存在重复的元素,实现类有HashSet、TreeSet等。
- Map(Key值搜索):键值对存储,Key值不能重复,但能引用相同的对象,实现类有HashMap、ConcurrentHashMap等。
2. HashMap和HashTable的区别
答:从4个方面作答。
- 线程安全:HashMap线程不安全;HashTable内部使用synchronized关键字,线程安全。
- Null Key支持:HashMap中null可作为Key;HashTable中null不能作为key也不能作为value。
- 效率:HashMap比HashTable效率高一点,而且HashTable基本淘汰了。
- 底层数据结构:JDK1.8之后HashMap在链表长度大于8时,将链表转为红黑树,以减少搜索时间。
2.1 HashMap线程不安全的解析
答:解释HashMap和HashTable。
- HashMap线程不安全是因为多线程环境下扩容可能会造成死循环。
- HashTable线程安全是因为内部实现put和remove方法时使用synchronized同步,所以对单个方法的使用是线程安全的。但对多个方法复合操作时,无法保证安全性。
2.2 Java集合中的快速失败机制
答:快速失败(fast-fail)时Java集合中的错误检测机制,当多个线程对集合进行结构层面的改变操作时,可能触发此机制。底层是通过迭代器遍历下一个元素之前都会检测modCount变量是否为exceptedModCount值,不是就抛出异常。
2.3 HashMap的底层结构
答:JDK1.8之后使用数组+链表+红黑树实现,解决链表过长而查询速度变慢。
2.4 HashMap的初始容量、加载因子、扩容增量和扩容步骤
答:HashMap的初始容量16,加载因子0.75,扩容增量是原容量的1倍。含义是HashMap中的元素个数超过16*0.75=12个后进行扩容。将创建原来HashMap大小两倍的bucket数组,将原来的对象放入新的bucket数组中,即rehash。
2.5 HashMap的长度为什么是2的幂
答:有助于减少碰撞次数,提高查询效率。 hash%length==hash&(length-1)的前提是 length 是2^n。例子:length为15,则length-1为14,对应二进制为1110,进行与操作后,最后一位为0,则最后一位为1的位置都不能存放元素。
2.6 Hash冲突解决办法
答:拉链法,线性探测再散列,二次探测再散列,伪随机探测再散列。
2.7 一致性哈希
TODO
3. ConcurrentHashMap和Hashtable的区别
答:主要体现在线程安全的方式上。
- 底层数据结构:ConcurrentHashMap是数组+链表/红黑树,Hashtable是数组+链表。
- 实现线程安全:
- 在JDK1.8之前,ConcurrentHashMap使用分段锁将Hash表分割为16个桶,每一把锁只锁当前操作用上的桶。
- 在JDK1.8之后,使用Node数组+链表+红黑树的数据结构,并发控制用synchronized和CAS操作。
- Hashtable使用synchronized保证线程安全,即用同一把锁来保护访问操作。
4. TreeMap特性
答:TreeMap底层使用红黑树实现,存储的键值按键排序。
- Key值存的是字符串等基础类型,按字典序排序
- Key值存的是自定义类型,需要类对象实现Comparable接口,并重写compareTo方法;或者创建TreeMap时指定比较器。
// 方式一:定义该类的时候,就指定比较规则
class User implements Comparable{
@Override
public int compareTo(Object o) {
// 定义比较规则
return 0;
}
}
public static void main(String[] args) {
// 方式二:创建TreeMap的时候,可以指定比较规则
new TreeMap<User, Integer>(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
// 定义比较规则
return 0;
}
});
}
4.1 Comparable和Comparator的区别
答:总结为:
- comparable接口在java.lang包,自定义比较规则时必须重写compareTo方法。
- comparator接口在java.util包,在创建TreeMap时需要传入一个自定义规则的比较器。
5. ArrayList和LinkedList区别
答:总结如下:
- 底层数据结构:ArrayList底层是Object动态数组;LinkedList底层使用双向链表。
- 快速随机访问:ArrayList支持随机访问(相当于get(int index)方法);LinkedList不支持。
- 内存空间占用:ArrayList必须预留一定的空间;LinkedList要存储前驱后继和节点信息,开销大。
注:ArrayList和LinkedList都是非线程安全的,Vector保证线程安全所有方法都是同步,但耗费大量时间,基本被抛弃了。
6. HashSet,HashMap和TreeSet区别
答:总结如下:
- HashSet底层使用Hash表实现,通过元素的hashCode值和equals()方法保证元素唯一性;
- TreeSet底层使用红黑树实现,通过comparable或comparator接口保证元素唯一性;
- HashSet底层是基于HashMap实现的,基本都是调用hashmap的方法。
7. LinkedHashMap和LinkedHashSet
答:LinkedHashMap能记录元素的插入顺序和访问顺序。具体实现:内部通过双向链表,保证元素的插入顺序。内部使用LRU(最近最少使用)算法。LinkedHashSet底层使用LinkedHashMap实现,二者关系类似HashMap和HashSet。
8. Iterator和ListIterator区别
答:这两种迭代器区别如下:
- Iterator能遍历list和set集合;ListIterator只能遍历list集合。
- Iterator只能后向遍历;ListIterator前向后向都可。
- ListIterator也是继承了Iterator,再添加新的功能。
9. 数组和List的转换
答:Arrays.asList和List.toArray方法。注意事项:
- Arrays.asList转换后,不能修改集合,因为转化后本质上仍属一个数组,是Arrays内部的ArrayList类。
- List.toArray转换,无参方法默认返回值是Object[]类,有参方法应传入list.size()即list大小的数组,以防止预留的空间大小不够被重新分配内存空间。