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

相關文章
相關標籤/搜索