Java集合精选常见面试题(13)

HashEntry 数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 的锁 。
JDK1.8 (上面有示意图)ConcurrentHashMap 取消了 Segment 分段锁,采用 CASsynchronized 来保证并发安全 。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树 。Java 8 在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N)))
synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍 。
Java集合使用注意事项总结这篇文章根据《阿里巴巴 Java 开发手册》总结了关于集合使用常见的注意事项以及其具体原理 。
强烈建议小伙伴们多多阅读几遍,避免自己写代码的时候出现这些低级的问题
1. 集合判空《阿里巴巴 Java 开发手册》的描述如下:

判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size()==0 的方式 。
这是因为 isEmpty() 方法的可读性更好,并且时间复杂度为 O(1) 。
绝大部分我们使用的集合的 size() 方法的时间复杂度也是 O(1),不过,也有很多复杂度不是 O(1) 的,比如 java.util.concurrent 包下的某些集合(ConcurrentLinkedQueueConcurrentHashMap...) 。
下面是 ConcurrentHashMapsize() 方法和 isEmpty() 方法的源码 。
public int size() {long n = sumCount();return ((n < 0L) ? 0 :(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int)n);}final long sumCount() {CounterCell[] as = counterCells; CounterCell a;long sum = baseCount;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)sum += a.value;}}return sum;}public boolean isEmpty() {return sumCount() <= 0L; // ignore transient negative values}2. 集合转 Map《阿里巴巴 Java 开发手册》的描述如下:
在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要注意当 value 为 null 时会抛 空指针异常 。
class Person {private String name;private String phoneNumber;// getters and setters}List<Person> bookList = new ArrayList<>();bookList.add(new Person("jack", "18163138123"));bookList.add(new Person("martin", null));// 空指针异常bookList.stream().collect(Collectors.toMap(Person::getName,Person::getPhoneNumber));下面我们来解释一下原因 。
首先,我们来看 java.util.stream.Collectors 类的 toMap() 方法 ,可以看到其内部调用了 Map 接口的 merge() 方法 。
public static <T,K,U,M extends Map<K,U>>Collector<T,?,M> toMap(Function<? super T,? extends K> keyMapper,Function<? super T,? extends U> valueMapper,BinaryOperator<U> mergeFunction,Supplier<M> mapSupplier) {BiConsumer<M,T> accumulator= (map,element) -> map.merge(keyMapper.apply(element),valueMapper.apply(element),mergeFunction);return new CollectorImpl<>(mapSupplier,accumulator,mapMerger(mergeFunction),CH_ID);}Map 接口的 merge() 方法如下,这个方法是接口中的默认实现 。
如果你还不了解 Java 8 新特性的话,请看这篇文章:《Java8 新特性总结》(opens new window)。
default V merge(K key,V value,BiFunction<? super V,? super V,? extends V> remappingFunction) {Objects.requireNonNull(remappingFunction);Objects.requireNonNull(value);V oldValue = https://www.huyubaike.com/biancheng/get(key);V newValue = (oldValue == null) ? value :remappingFunction.apply(oldValue,value);if(newValue == null) {remove(key);} else {put(key,newValue);}return newValue;}

经验总结扩展阅读