Java开发过程中,集合使用的一些问题记录

问题记录

集合是每个开发者一定要使用的工具类,以下记录几点常遇到的问题

1.Arrays.asList

Arrays.asList(E)得到的List是一个内部类java.util.Arrays.ArrayList


没有定义add和remove方法,而且定义的同时List的实际引用就是数组本身。

所以修改转换后的List和数组效果都是双向的。

由基本类型数组转换来的List存放的是数组的引用,list只有一个元素


由于没有定义add和remove等,强行调用会报错,究其原因就是因为List保存的是数组的引用,数组定义后便固定长度,对数组进行长度操作都会报错

2.不可变List

通过static final 定义的常量仍然可以被修改

暴露出去的常量使用Collections.unmodifiableList(E)控制,当被尝试修改时会抛出java.lang.UnsupportedOperationException

3.初始化问题

List定义时初始化



千万不能使用list.add(),因为list还没有初始化出来
map同理可以直接put

4.forEach

Stream中的forEach是对迭代器简单的封装,接受一个Consumer对象
其中Consumer还能返回一个Consumer对象以达到串行化。

  public static void main(String[] args) {
    ConsumerTest02 test = new ConsumerTest02();
    List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    test.print(list, item -> System.out.print(" consumer1:" + item * 2), item -> System.out.print(" consumer2:" + item * 3));
}

    /*
      andThen方法, 将参数传递给调用者执行accept方法,然后再传给第二个consumer执行accept方法。
    */
  public void print(List<Integer> list, IntConsumer con1, IntConsumer con2) {
    list.forEach(item -> con1.andThen(con2).accept(item));
}

这样就可以在一次循环中处理多个业务内容
这个时候就有人问了,那我都写在一个Consumer不行吗干嘛要分开多麻烦
这种处理方式就像是流水线,前面的人加工完到后面的人加工下一步,一步到胃当然可以,但是流程就缺乏管理,有问题不容易查找。
分开之后流程清晰,看每一步处理的结果就能判断问题所在。
当然如果也不并不复杂不需要这样分开,反而适得其反。

另一方面,forEach不能被打断,如果有需要打断的场景,还是用回for循环

5.equals和hashcode

重写equals()方法就必须重写hashCode()方法主要是针对HashSet和Map集合类型,而对于List集合倒没什么影响。

原因: 在向HashSet集合中存入一个元素时,HashSet会调用该对象(存入对象)的hashCode()方法来得到该对象的hashCode()值,然后根据该hashCode值决定该对象在HashSet中存储的位置。简单的说:HashSet集合判断两个元素相等的标准是:两个对象通过equals()方法比较相等,并且两个对象的HashCode()方法返回值也相等。如果两个元素通过equals()方法比较返回true,但是它们的hashCode()方法返回值不同,HashSet会把它们存储在不同的位置,依然可以添加成功。

这就是问题所在:就是如果你只重写equals()方法,而不重写hashCode(),如果equals()为true,而它们的hashCode()方法返回值肯定不一样,因为它们都不是同一对象所以内存地址肯定不一样,所以它还是添加成功了,那么其实你写的equals()方法根本没用。

6.Stream中groupBy

对集合进行分组这是常用的操作
如果想要进行对多字段进行分组,可以通过字符串拼接的方式
list.stream().collect(Collectors.groupingBy(user -> user.getName() + "_" + user.getId()));
注意为空的处理
同时,如果想保持分组后的map有序,可以使用Treemap或者LinkedHashMap
一个是优先排序,一个是按插入顺序排序
LinkedHashMap<String, List<Score>> scores = list.stream().collect(Collectors.groupingBy(Score::getName, LinkedHashMap::new, Collectors.toList()));// 按照姓名分组
多级分组: 嵌套使用groupingBy即可

List<String> views = Lists.newArrayList("wsbsq","hello word","b8fw", "word", "wall", "ad");  
Map<Object, Map<Object, List<String>>> res = views.stream().collect(groupingBy(str ->str.charAt(0),groupingBy(String::length)));  
// {a={2=[ad]}, b={4=[b8fw]}, w={4=[word, wall], 5=[wsbsq]}, h={10=[hello word]}}  
7.stream中的reduce

reduce一般是接受一个BinaryOperator类型的lambada表达式,因为计算结果可能为空所以返回一个Optional

List<Integer> numList = Arrays.asList(1,2,3,4,5);  
int result = numList.stream().reduce((a,b) -> a + b ).get();  

或者可以在reduce时给定一个初始值
int result = numList.stream().reduce(0,(a,b) -> a + b );
这样即使list中无法计算出来,也会至少有0
reduce每次都会返回当前节点和上一次处理的结果,所以还能做到像这样的操作

List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6);
ArrayList<String> result = numList.stream().reduce(new ArrayList<String>(), (a, b) -> {
    a.add("element-" + Integer.toString(b));
    return a;
}, (a, b) -> null);
System.out.println(result);
//[element-1, element-2, element-3, element-4, element-5, element-6]

按照自己的逻辑处理每一个数据,可以改变数据结构就连size都可以不一样

未完待续