Appearance
1.Collection和Collections有什么区别?
- Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。
- Collections是一个包装类,它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
3.在遍历时删除List集合中的数据有几种方式?
- 通过普通的for循环,如果删除需要做一次i--;只能通过内容删,不可以通过索引删除。
- 使用迭代器,使用Iterator的remove方法移除当前对象,不能使用List.remove
- 将原来的copy一份副本,遍历原来的list,然后删除副本(fail-safe)
- 使用并发安全的集合类
- 通过Stream的过滤方法
- 通过removeIf方法
- 详见如何在遍历时删除集合的数据
4.Set是如何保证元素不重复的?
- TreeSet是二叉树实现的,TreeSet中的数据是自动排好序的,不允许放入null值;底层基于TreeMap,TreeMap基于红黑树。
- HashSet是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束;底层基于HashMap
5.HashSet,TreeSet,LinkedHashSet有何区别
- HashSet:最简单的Set,只提供去重的能力
- LinkedHashSet:和HashSet相比它是顺序的,会记录插入的顺序,性能比HashSet差点
- TreeSet:提供去重和排序的能力
6.ArrayList、LinkedList与Vector的区别?
- ArrayList:底层是数组,适用于get/set多的场景
- LinkedList:底层是双向链表,更适合添加/删除多的场景
- Vector:相当于加锁的ArrayList
7.ArrayList初始容量是多少?内部是如何扩容的?为什么扩充这么多?
JDK1.7之前ArrayList默认大小是10,JDk1.7之后是0,在第一次插入数据时变为0.
- 检查新增元素后是否会超过数组的容量,如果超过,则进行下一步扩容
- 设置新的容量为老容量的1.5倍,最多不超过
2^31-1
,初始10下一次就是15int newCapacity = oldCapacity + (oldCapacity >> 1);
- 创建新数据,先将老数据复制到新数组,再插入新数据替换掉之前的数组,之前的数组交给GC回收,扩容完成
- 因为
>>1
相当于除以2,使用位运算效率比除法更高。
8.ArrayList的subList方法有什么需要注意的地方吗?
subList是List接口中定义的一个方法,该方法主要用于返回一个集合中的一段。
- SubList是ArrayList的内部类,没有继承关系,所以SubList无法直接强制转换为ArrayList或其他List。
- SubList返回的是一个视图,它只是将List中的部分属性直接赋值给了自己,如果需要修改等,可以复制
java
subList = Lists.newArrayList(subList);
list.stream().skip(strart).limit(end).collect(Collectors.toList());
9.HashMap、Hashtable和ConcurrentHashMap的区别?
- HashMap是非线程安全的,Hashtable和ConcurrentHashMap是线程安全的。
- HashTable和ConcurrentHashMap的key和value都不允许出现null值,HashMap对null无限制。
- HashMap底层是基于数组+链表,HashTable底层是哈希表
10.Hashtable和ConcurrentHashMap如何实现线程安全?
Hashtable中的方法是同步的,所以它是线程安全的。
ConcurrentHashMap使用分段锁和“CAS+Synchronized”机制,在插入时
- 如果此段没有数据,则使用CAS插入
- 如果有数据则使用
Synchronized
锁住第一个节点
11.HashMap、Hashtable和ConcurrentHashMap的扩容机制?
- HashMap默认16,当元素个数超过容量的75%时会扩大到原来的2倍;
- Hashtable默认11,当元素个数超过容量的75%时会扩大到原来的2倍+1;
- ConcurrentHashMap默认16,当元素个数超过容量的75%时会扩大到原来的2倍,且采用分段锁,每个段独立进行扩容操作,避免了整个ConcurrentHashMap的锁竞争。
12.介绍下hashcode和equals以及其使用场景
- hashcode:返回一个int类型数字,根据一定的hash规则(存储地址,字段,长度等),映射成一个数组,即散列值
- equals:顶级类Object里面的方法,所有的类都是继承Object,根据匹配规则返回boolean用于匹配对象是否相同,常用逻辑
- 地址是否一样
- 非空判断和Class类型判断
- 强转
- 对象里面的字段一一匹配
- 使用场景:对象比较、或者集合容器里面排重、比较、排序
java
public class User {
private int age;
private String name;
private Date time;
// get&&set
@Override
public int hashCode() {
return Objects.hash(age,name,time);
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return age == user.age && Objects.equals(name, user.name) && Objects.equals(time, user.time);
}
}
13.HashMap和TreeMap应该怎么选择?
TreeMap适用于需要排序的情况,可自定义排序列表,底层是平衡二叉树,一般性能比HashMap差;而HashMap底层是数组+链表。
14.如果Map需要排序,该如何排序?
按照添加顺序使用LinkedHashMap,按照自然排序使用TreeMap,自定义排序 TreeMap(Comparetor c)
15.需要线程安全且效率更高的Map该如何选择?
使用ConcurrentHashMap
,其线程安全且效率比Hashtable更高
16.看过HashMap的源码吗?其底层是如何实现的?
数组+(链表/红黑树),当链表长度>8会转换成红黑树
17.HashMap内部的bucket数组长度为什么一直都是2的整数次幂?
HashMap计算出Hash值后,使用&与运算(都是1才是1)代替取模%运算,而使用&运算必须要是2的整数次幂-1
(转成二进制全是1)才能实现取模%运算的效果。
比如50 & 15 = 50 % 15 = 2
18.为什么ConcurrentHashMap不允许null值?
当一个线程将null值插入到ConcurrentHashMap中时,其他线程可能无法正确判断该位置是否有值,
19.设计一个高效的并发数组
仿CopyOnWriteArrayList
,对于修改操作,使用synchronized
;对于查询操作,使用volatile
;
修改数据时进行复制,更新完成后进行替换,也就是说不会影响读操作,进行读取时也不需要加锁。