Java集合类包括Map和Collection。Collection包括List、Set和Queue三个子类。》如下图所示:《本文带你解读源码:集合使用中的常见问题,学习一些优秀的设计思想。《集合批量操作性能》集合的单次操作一般没有性能问题,性能问题主要出现在批量操作中。比如批量添加操作:当List和Map添加大量数据时,使用for循环+add/put方式添加新数据。这会造成很大的扩容成本。我们应该尝试使用addAll和putAll方法来添加新数据。两种方案的性能对比展示如下:单个for循环增加300w,耗时1518小时。批量加300w需要8个小时。可以看出,批量添加方式的性能是单次添加方式的189倍。主要原因是批量添加的方式只会扩容一次,大大缩短了运行时间,而单次添加的方式每次都会达到扩容阈值。会扩容,整个过程不断扩容,浪费很多时间。我们看一下批量添加的源码:可以看到在整个批量添加的过程中,只扩容了一次。《集合线程安全》集合的非线程安全是指:集合类作为共享变量,在多线程读写时是不安全的。如果要实现线程安全的集合,在类注解中,JDK推荐我们使用Collections.synchronized*类。Collections帮我们实现了List、Set、Map对应的线程安全的方法,如下图所示:从源码中可以看出,Collections通过给List操作数组的方法加锁来实现线程安全同步关键字。集合方法常见问题List《Arrays.asList()方法》我们在将数组转为集合时,经常会用到Arrays.asList(array)。这种方法有两个问题。代码演示如下:问题一:修改数组的值会直接影响到原来的列表。publicvoidtestArrayToList(){Integer[]array=newInteger[]{1,2,3,4,5,6};Listlist=Arrays.asList(数组);//问题一:修改数组值会直接影响原列表log.info("修改数组前,集合第一个元素为:{}",list.get(0));数组[0]=10;log.info("数组被修改前,集合的第一个元素为:{}",list.get(0));}问题2:无法对新的List进行add、remove等操作,否则会在运行时报UnsupportedOperationException错误。publicvoidtestArrayToList(){Integer[]array=newInteger[]{1,2,3,4,5,6};Listlist=Arrays.asList(数组);//问题2:使用add,removed等方法操作列表时,//会报UnsupportedOperationException。list.add(7);}原因分析:从上图我们可以发现Arrays.asList方法返回的List不是java.util.ArrayList,它本身就是一个内部的静态类,直接持有引用数组,并没有实现添加和删除等方法。这些就是问题1和2的原因。“list.toArray方法”publicvoidtestListToArray(){Listlist=newArrayList(){{add(1);添加(2);添加(3);添加(4);}};//下面这行代码不能转为数组,不带参数的toArray返回Object[],//不能向下转为List,编译不能通过//Listlist2=list.toArray();//有个参数toArray方法,当数组大小不够时,数组为nullInteger[]array0=newInteger[2];list.toArray(array0);log.info("toArray数组大小不够,array0array[0]的值为{},array[1]的值为{},",array0[0],array0[1]);//数组的初始大小刚刚好,只是转换成Integer[]的数组array1=newInteger[list.size()];list.toArray(array1);log.info("toArray数组大小刚好,array1数组[3]值为{}",array1[3]);//数组初始化大小大于实际需要的大小,也可以转为数组Integer[]array2=newInteger[list.size()+2];list.toArray(array2);log.info("toArrayarraysizeistoolarge,array2array[3]valueis{},valueofarray[4]is{}",array2[3],array2[4]);}toArray数组大小不够,array0array[0]值为null,array[1]值为null,toArray数组大小刚刚好,array1array[3]值为4toArray数组大小太多,array2array[3]值为4,数组[4]value为null的原因分析:toArray的无参方法不能强行变成具体的类型。编译的时候会有提示。我们通常使用带参数的toArray方法。这时候就有坑了。如果参数数组的大小不够,此时返回的数组值为空。“Collections.emptyList()方法”问题:在返回的Collections.emptyList()上调用了add()方法;并抛出UnsupportedOperationException。分析:Collections.emptyList()返回一个不可变的空列表。这个空列表对应的类型是EmptyList。该类是Collections中的静态内部类,继承了AbstractList。AbstractList中默认的add方法没有实现,直接抛出UnsupportedOperationException。EmptyList只是继承了AbstractList,并没有重写add方法,所以直接调用add方法会抛出异常。除了emptyList,还有emptySet、emptyMap等。“List.subList()方法”list.subList()生成的集合也会和原来的List进行交互。推荐使用Listlist=Lists.newArrayList(arrays);生成新列表,不操作原列表。「UnmodifiableList」UnmodifiableList是Collections中的一个内部类。通过调用Collections.unmodifiableList(Listlist),可以返回指定集合的??不可变集合。集合只能读取,不能增删改,保护了不可变集合的安全性。但这种不变性只是积极的不变性。反之,如果原始集合被修改,不可变集合仍然会被同步修改。因为不可变集合底层还是原来的List。Map"ConcurrentHashMapisnotallowedtobenull"ConcurrentHashMap#put方法的源码,一开始看到的是对KV的null检查。为什么ConcurrentHashMap和HashMap设计的判断逻辑不一样?DougLea先生的解释是:null会造成歧义。如果value为null,我们无法知道value是null还是key没有映射到具体的value?DougLea不喜欢null,认为null是一个隐藏的炸弹。贴一个普通的Map子类集合用于null存储:“HashMap是无序的”例子:importjava.util.HashMap;publicclassApp{publicstaticvoidmain(String[]args){HashMapresult=getList();result.forEach((k,v)->{System.out.println(k+":"+v);});}//查询方法(简化版)publicstaticHashMapgetList(){HashMapresult=newHashMap<>();//最终返回的结果集//伪代码:从数据库中查询数据,然后对数据进行处理,保存Atfor(inti=1;i<=5;i++){result.put("2022-"+i,"你好java"+i);}返回结果;}}结果没有按顺序返回。原因分析HashMap是以hash方式存储的,所以存储和读取的顺序可能不一致。这也就意味着HashMap是一个无序集合,所以插入的顺序会和最终显示的顺序不一致。解决方法:把无序的HashMap改成有序的LinkedHashMap。LinkedHashMap是HashMap的子类,所以LinkedHashMap除了具有HashMap的所有特性外,还有一些自己的扩展属性,包括在LinkedHashMap中额外维护了一个双向链表,用于保存(插入)元素的顺序.Set如果我们需要对自定义对象进行去重,我们需要重写hashCode和equals方法。否则,HashSet调用默认的hashCode方法来确定对象的地址,就达不到根据对象的值去重的目的。