Java Collection 移除元素的几种方式

1. 前言

操做集合是一个 Java 编程人员几乎天天都在重复的事情。今天咱们来研究一下从 Java Collection 中删除元素的方法。我构建了一个简单的集合,咱们以此为例子来展开探索。html

List<String> servers = new ArrayList<>();
        servers.add("Felordcn");
        servers.add("Tomcat");
        servers.add("Jetty");
        servers.add("Undertow");
        servers.add("Resin");

2. for 循环并不必定能从集合中移除元素

让咱们使用传统的 foreach 循环移除 F 开头的假服务器,可是你会发现这种操做引起了 ConcurrentModificationException 异常。java

// 错误的示范 千万不要使用
  for (String server : servers) {
    if (server.startsWith("F")) {
        servers.remove(server);
    }
 }

难道 for 循环就不能移除元素了吗?固然不是!咱们若是能肯定须要被移除的元素的索引仍是能够的。编程

// 这种方式是可行
 for (int i = 0; i < servers.size(); i++) {
    if (servers.get(i).startsWith("F")) {
        servers.remove(i);
    }
}

可是这种方式我目前只演示了 ArrayList,其它的类型并无严格测试,留给你本身探索。segmentfault

3. 迭代器 Iterator 能够删除集合中的元素

在传统方式中咱们使用 Iterator 是能够保证删除元素的:api

Iterator<String> iterator = servers.iterator();

        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next.startsWith("F")) {
                iterator.remove();
            }
        }

4. 遍历删除元素的缺点

  • 咱们须要遍历集合的每个元素并对它们进行断言,哪怕你删除一个元素。
  • 尽管咱们能够经过迭代的方式删除特定的元素,可是操做繁琐,根据集合类型的不一样有潜在的 ConcurrentModificationException 异常。
  • 根据数据结构的不一样,删除元素的时间复杂度也大大不一样。好比数组结构的 ArrayList 在删除元素的速度上不如链表结构的 LinkedList

5. 新的集合元素删除操做

Java 8 提供了新的集合操做 APIStream 来帮助咱们解决这个问题。我在之前的文章中已经介绍了 Java 8 Stream API,若是有兴趣能够去看看。数组

5.1 Collection.removeIf()

新的 Collection Api removeIf(Predicate<? super E> filter) 。该 Api 提供了一种更简洁的使用 Predicate (断言)删除元素的方法,因而咱们能够更加简洁的实现开始的需求:服务器

servers.removeIf(s-> s.startsWith("F"));

同时根据测试,ArrayListLinkedList 的性能接近。通常推荐使用这种方式进行操做。数据结构

5.2 Stream 实现移除元素

和上面全部移除操做不一样的是,其实任何操做都不会改变 Stream 源,咱们仅仅是使用 Stream Api 操做数据源的副本。遵循了 数据源 -> 中间操做 -> 概括终止 的生命周期。咱们来看看使用 Stream 如何实现咱们的意图。性能

5.2.1 经过 filter 断言实现

咱们能够使用 Streamfilter 断言。filter 断言会把符合断言的流元素聚集成一个新的流,而后概括起来便可,因而咱们能够这么写:测试

// 跟以上不一样的是 该方式中的断言是取反的操做。
List<String> newServers = servers.stream().filter(s -> !s.startsWith("F")).collect(Collectors.toList());

这个优势上面已经说了不会影响原始数据,生成的是一个副本。缺点就是可能会有内存占用问题。

5.2.2 经过 Collectors.partitioningBy 概括

这种方法虽然能够知足须要可是我感受有点投机取巧的成份。Collectors.partitioningBy() 方法本意是作二分类的。该方法会将流中符合断言的、不符合断言的元素分别概括到两个 key 分别为 truefalseMap 中,咱们能够归类获得符合和不符合的元素集。实现以下:

Map<Boolean, List<String>> f = servers.stream().collect(Collectors.partitioningBy(s -> !s.startsWith("F")));
        
  List<String> trues = f.get(Boolean.TRUE);
  System.out.println("不以 F 开头的:  " + trues);

  List<String> falses = f.get(Boolean.FALSE);
  System.out.println("以 F 开头的:  " + falses);

通常该方式不推荐在此场景使用,并不符合该 Api 的设计意图。

6. 总结

今天咱们研究了一些从 Collections 中删除元素的方法 及其注意事项。不知道你有没有其它的实现方式,不妨经过公众号:Felordcn 告诉我。

关注公众号:Felordcn 获取更多资讯

我的博客:https://felord.cn

相关文章
相关标签/搜索