本文主要参考Java面试进阶指南(https://xiaozhuanlan.com/topic/8590263147)、https://www.jianshu.com/p/8324a34577a0?utm_source=oschina-app
本文主要内容:
- Set、List、Map简介,collection和collections的区别
- ArrayList、LinkedList、Vector的异同
- HashMap、HashTable异同
- comparable和comparator的区别
- HashMap实现原理
一、分别介绍Set、List、Map
1.Set:集合中的元素不允许重复
- HashSet(无序、唯一):基于HashMap实现
- LinkedHashSet:继承于HashSet
- TreeSet(有序,唯一:红黑树)
2.List:存储不唯一的有序对象
- ArrayList:Object数组
- Vectot:Object数组
- LinkedList:双向链表
3.Map:使用键值对存储,值可以重复,但键是唯一的。
- HashMap:JDK1.8之前HashMap由数组+链表组成的,JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
- LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
- HashTable:数组+链表
- TreeMap:红黑树
二、Collection和Collections的区别?
Collection是一个集合接口,提供了对集合对象进行基本操作的通用接口方法。
Collections是针对集合类的一个包装类,提供了一系列静态方法以实现对各种集合的搜索、排序、线程安全化等操作。
三、ArrayList和LinkedList的异同?
1.线程安全:LinkedList和 ArrayList都是不同步的,无法保证线程安全
2.底层实现:ArrayList是基于动态数组(Object[] array)实现的,每个元素在内存地址中连续。ArrayList是基于双向链表实现的。
3.插入删除效率对比:若在列表头部或是中间插入或删除元素时,ListedList性能优于ArrayList,原因是ArrayList采用数组实现,需要移动元素的索引和数组扩容;若在列表尾部增加或删除元素,二者性能差别不大。
4.随机快速访问对比:ArrayList支持高效的随机元素访问,LinkedList不支持。
5.内存空间对比:ArrayList的空间浪费主要体现在list列表的结尾预留一定的容量空间,LinkedList体现在它的每一个元素都要消耗比ArrayList更多的空间(存放后继和前驱节点指针和数据)。
四、ArrayList和Vector的异同?
1.ArrayList和Vector都是基于数组实现的。
2.Vector是线程安全的,ArrayList不是线程安全的。单线程时,性能上,Vector逊于ArrayList。
五、ArrayList扩容机制
1.经典笔试题:ArrayList list = new ArrayList(20);中的list扩充几次? 答案:0次
2.ArrayList提供了三种初始化方法:
public ArrayList() //默认提供大小为10的数组
public ArrayList(Collection<? extends E> c) //将一个集合所谓作为list元素,size即为集合的长度
public ArrayList(int initialCapacity);//指定初始化容量为initialCapacity的数组
3.当需要增加元素导致空间不够时,按照1.5倍的大小进行扩容
六、HashMap和HashTable的异同、HashMap长度为何是2的幂次方
1.线程安全:HashMap不是线程安全的,HashTable是线程安全的。(当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。 )
2.效率:因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它。
3.是否允许null键值:HashMap最多允许一个键为null;可以有一个或多个值为空。HashTable都不允许null。
4.初试容量、扩容方式:若创建时未指定大小,HashMap默认为16,之后每次扩容,大小为原来的2倍;HashTable默认为11,之后每次扩容,为原来的2n+1倍。若创建时给定了初始容量,HashTable直接使用给定大小,HashMap给定2的幂次方大小。(之所以会有这样的不同,是因为Hashtable和HashMap设计时的侧重点不同。Hashtable的侧重点是哈希的结果更加均匀,使得哈希冲突减少。当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀。而HashMap则更加关注hash的计算效率问题。在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。当然这引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改动。)
5.底层数据结构实现:二者底层都采取数据+链表结构实现,但JDK1.8以后,HashMap为解决Hash冲突,当链表长度大于一定值以后,将链表转为红黑树,以减少搜索时间。
七、HashMap和HashSet的异同
HashSet实现了Set接口,它不允许集合中出现重复元素。当我们提到HashSet时,第一件事就是在将对象存储在
HashSet之前,要确保重写hashCode()方法和equals()方法,这样才能比较对象的值是否相等,确保集合中没有
储存相同的对象。如果不重写上述两个方法,那么将使用下面方法默认实现:
public boolean add(Object obj)方法用在Set添加元素时,如果元素值重复时返回 "false",如果添加成功则返回"true"
八、HashSet如何检查重复
当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。
九、HashMap实现原理
1.JDK1.7与1.8HashMap实现原理的变化:
JDK1.8底层实现时将链表转换为红黑树,以加快检索时间
2.hash值计算方式变化,1.8版本有效减少了hash碰撞的发生
参考:https://zhuanlan.zhihu.com/p/21673805
十、HashMap线程不安全的原因
多线程操作下容易出现死循环,本质:并发执行put()操作导致出发扩容行为,从而导致环形链表,是的在获取数据遍历链表时形成死循环。JDK1.8后解决了这个问题,不过还是线程不安全的,因为没加同步锁。
十一、comparable 和 Comparator的区别
1.comparator示例:
ArrayList<Integer> alist = new ArrayList<>();
alist.add(1);
alist.add(-3);
alist.add(6);
alist.add(4);
alist.add(5);
System.out.println("原始list");
System.out.println(alist);
Collections.sort(alist);
System.out.println("Collections默认排序:");
System.out.println(alist);
Collections.sort(alist, new Comparator<Integer>() { //定制降序排列
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
System.out.println("实现Compartor降序排序");
System.out.println(alist);
输出:
原始list
[1, -3, 6, 4, 5]
Collections默认排序:
[-3, 1, 4, 5, 6]
实现Compartor降序排序
[6, 5, 4, 1, -3]
2.comparable示例:
public class TestComparable {
public static void main(String[] args) {
Person p1 = new Person(20, "Tony");
Person p2 = new Person(15, "Andy");
Person p3 = new Person(25, "Harry");
ArrayList<Person> alist = new ArrayList<>();
alist.add(p1);
alist.add(p2);
alist.add(p3);
Collections.sort(alist);
System.out.println(alist);
}
}
class Person implements Comparable<Person>{
private Integer age;
private String name;
public Person(Integer age, String name) {
super();
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Person o) {
if(this.age > o.age) {
return 1;
}else if(this.age < o.age){
return -1;
}
return 0;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
输出:
[Person [age=15, name=Andy], Person [age=20, name=Tony], Person [age=25, name=Harry]]