JavaGuide知识点整理——集合使用注意事项总结

这篇文章是根据阿里巴巴java开发手册总结了关于集合使用常见的逐一实现以及原理。

集合判空

判断所有集合内部的元素是否为空使用isEmpty()方法,而不是size()==0的方式。

这是因为isEmpty() 方法的可读性更好,并且时间复杂度为O(1).不过也有很多复杂度不是O(1)的,比如JUC包下的某些集合ConcurrentLinkedQueue 、ConcurrentHashMap...
下面是ConcurrentHashMap的size()方法和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
}

集合转Map

在使用java.util.stream.Collectors类的toMap()方法转Map集合时,一定要注意value为null时会抛出空指针异常。

下面我们来解释一下原因:
首先,java.util.stream.Collectors类的toMap()方法可以看到其内部调用了Map接口的merge()方法。


Collectors源码
merge方法

而merge方法源码如下:


merge方法中判空

所以由上可以看出如果value值为空会报空指针。

集合遍历

不要再foreach循环里进行元素的remove/add操作。remove元素请使用Irerator方式,如果并发操作,需要对Iterator对象加锁。

通过反编译你会发现foreach语法糖底层其实还是以来Iterator,不过remove/add操作直接调用的是集合自己的方法,而不会Iterator的remove/add方法。

这就导致Iterator莫名其妙的发现自己的元素被remove/add,然后就会抛出一个并发修改的异常(ConcurrentModificationException).这就是单线程状态线上线下产生的fail-fast机制。

fail-fast机制:多个线程对fail-fast集合进行修改的时候,可能抛出ConcurrentModificationException。即使是单线程的情况下也有可能会出现这种情况。
除了上面介绍的直接使用Iterator进行遍历操作以外,还可以:

  • 使用普通的for循环
  • 使用fail-safe的集合类。java.util包下的所有的集合类都是fail-fast的,而java.util.concurrent包下的所有类都是fail-safe的。

集合去重

可以利用Set元素唯一的特性,快速对一个集合进行去重操作,避免使用List的contains()进行遍历去重或者判断包含操作。
这里用HashSet和ArrayList为例说明。

// Set 去重代码示例
public static  Set removeDuplicateBySet(List data) {

    if (CollectionUtils.isEmpty(data)) {
        return new HashSet<>();
    }
    return new HashSet<>(data);
}

// List 去重代码示例
public static  List removeDuplicateByList(List data) {

    if (CollectionUtils.isEmpty(data)) {
        return new ArrayList<>();

    }
    List result = new ArrayList<>(data.size());
    for (T current : data) {
        if (!result.contains(current)) {
            result.add(current);
        }
    }
    return result;
}

两者的核心在于contains()方法的实现。
HashSet的contains()方法底部以来HashMap的containsKey()方法。时间复杂度接近于O(1).
我们有n个元素插入Set,时间复杂度就是O(n).
ArrayList的contains()方法是通过遍历所有的元素来实现的。时间复杂度接近O(n).所以List有n个元素,时间复杂度是O(n^2)

集合转数组

使用集合转数组的方法,必须使用集合的toArray(),传入的是类型完全一致,长度为0的空数组。

String [] s= new String[]{
    "dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A"
};
List list = Arrays.asList(s);
Collections.reverse(list);
//没有指定类型的话会报错
s=list.toArray(new String[0]);

由于JVM优化,new String[0]作为Collection.toArray()方法的参数现在使用更好。new String[0]就是起了一个模板的作用,指定返回数组的类型,0是为了节省空间。

数组转集合

使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法。它的 add/remove/clear方法会抛出UnsupportedOperationException异常。

  1. Arrays.asList()是泛型方法,传递的数组必须是对象数组,不能是基本类型的。
    当传入一个原生数据类型的时候,Arrays.asList()的真正得到的参数就不是数组中的元素,而是数组对象本身。此时List的唯一元素就是这个数组。所以有如下代码:
int[] myArray = {1, 2, 3};
List myList = Arrays.asList(myArray);
System.out.println(myList.size());//1
System.out.println(myList.get(0));//数组地址值
System.out.println(myList.get(1));//报错:ArrayIndexOutOfBoundsException
int[] array = (int[]) myList.get(0);
System.out.println(array[0]);//1

我们可以用包装类解决这个问题。

  1. 使用集合的修改方法add,remove,clear会抛出异常
    因为Arrays.asList() 方法返回的并不是java.util.ArrayList,而是java.util.Arrays的一个内部类,这个内部类并没有实现(重写)集合的修改方法。

我们如何正确的将数组转化为ArrayList?

  1. 手动实现工具类
static  List arrayToList(final T[] array) {
  final List l = new ArrayList(array.length);

  for (final T s : array) {
    l.add(s);
  }
  return l;
}
Integer [] myArray = { 1, 2, 3 };
System.out.println(arrayToList(myArray).getClass());//class java.util.ArrayList
  1. 最简单的方法
List list = new ArrayList<>(Arrays.asList("a", "b", "c"));
  1. 使用java8的stream
Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本类型也可以实现转换(依赖boxed的装箱操作)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());
  1. 使用 工具类
List list = new ArrayList();
CollectionUtils.addAll(list, str);
  1. Java9的 List.of()方法
Integer[] array = {1, 2, 3};
List list = List.of(array);

本篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注,也祝大家工作顺顺利利!

你可能感兴趣的:(JavaGuide知识点整理——集合使用注意事项总结)