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

synchronized 来保证并发安全 。数据结构跟 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;}merge() 方法会先调用 Objects.requireNonNull() 方法判断 value 是否为空 。
public static <T> T requireNonNull(T obj) {if (obj == null)throw new NullPointerException();return obj;}解决方案使用Java8提供的Optional类优雅地处理空指针问题
//如果值为null则赋默认值空串Map<String, String> collect = bookList.stream().collect(Collectors.toMap(Person::getName, t -> Optional.ofNullable(t.getPhoneNumber()).orElse("")3. 集合遍历《阿里巴巴 Java 开发手册》的描述如下:
不要在 foreach 循环里进行元素的 remove/add 操作 。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁 。
通过反编译你会发现 foreach 语法糖底层其实还是依赖 Iterator。不过 ,  remove/add 操作直接调用的是集合自己的方法,而不是

推荐阅读