Java中List集合间的复制
一、概览
在这篇短文中,我们将向你展示把一个List复制到另一个List中去的不同方式以及在这个过程中常见的错误。
二、构造器
copy一个List最简单方式就是使用构造器,这个构造器能接收一个集合参数。
List copy = new ArrayList<>(list);
由于使用这种方式的内部机制是:复制对象的引用而不是clone对象,所以对对象的每一个修改都将同时影响两个list。
正式由于上述的原因,使用构造器这种方法比较适用于那些不可改变对象在list间的复制。
List copy = new ArrayList<>(list);
三、List的ConcurrentAccessException异常
在使用list时常见的问题就是ConcurrentAccessException,这也就是意味着:当我们复制的时候,
很可能在另一个线程中正在修改这个list。
要解决这个问题,我们不得不:
1、使用线程安全的集合
2、在迭代集合时,给这个集合加上合适的锁
3、寻找另一种方式,从而避免对原始集合进行复制
考虑到我们的最后一个方法并不是线程安全的,因此如果我们想用第一种选项解决这个问题,我们可以使用CopyOnWriteArrayList。
四、AddAll方法
复制集合元素的另一种方法就是使用addAll
List copy = new ArrayList<>();
copy.addAll(list);
这里一定要牢记,和使用构造器一样,在使用这个方法时,两个list中的内容都将引用的是相同对象。
五、Collections.copy
Collections类提供了一些专门用于操作集合的静态方法。其中一个就是copy。Collections.copy(dest,source),这个方法接收两个参数,一个是source list 和一个dest list(dest list的长度应大于source list)。这个方法的特点是: 它将保持每一个被copy元素在目标集合中的下标和它们在源集合中的下标一样。 即: 源集合中下标为1的元素会被复制到目标集合的下标为1的位置上。
List source = Arrays.asList(1,2,3);
List dest = Arrays.asList(4,5,6);
Collections.copy(dest, source);
这上面的这个例子中,之前在 dest list中的元素都会被覆盖,因为俩个集合的大小一样。
六、使用Java8
在Java8中,我们可以使用stream来解决这个问题:
List copy = list.stream()
.collect(Collectors.toList());
这种方式的优势是,我们有机会使用skip和filter。在下面的这个例子中,我们将使用skip方法来跳过
第一个元素:
List copy = list.stream()
.skip(1)
.collect(Collectors.toList());
我们也能通过String的长度来过滤或者对象的属性值:
List flowers = list.stream()
.filter(f -> f.getPetals() > 6)
.collect(Collectors.toList());
如果我们想以null-safe方式使用的话,可以这样做:
List flowers = Optional.ofNullable(list)
.map(List::stream)
.orElseGet(Stream::empty)
.collect(Collectors.toList());
并且我们也能在这种方式下跳过某个元素:
List flowers = Optional.ofNullable(list)
.map(List::stream).orElseGet(Stream::empty)
.skip(1)
.collect(Collectors.toList());
七、总结
文中我们探索了不同java版本中提供的把一个list复制到另一个list的多种方式,以及在此过程中的常见问题。