Java 使用 Stream API 篩選 List


前言

上課的時候看到老師用迭代器來遍歷 List 中的元素的時候,我的內心是極其嫌棄的,這種迭代方法不能直接訪問當前的元素,而且寫起來也麻煩。於是上網查了查 Java 有沒有類似於 Linq 的東西,雖然發現了一個 JLinq 但是抱着學習的心態,還是沒有用這個東西。看了看 Intellji 的自動補全然后想出了下面的代碼。

題目

刪除 List 中信息重復的學生

解法一

LinkedList<T3.Student> repo3 = new T3.StudentTest().getRepo();
repo3.removeIf(s->repo3.indexOf(s)!=repo3.lastIndexOf(s));

這個方法看起來是沒有很大的問題的,但是如果問題稍微變了一下,這就沒用了。

題目 update

刪除 List 中信息部分重復的學生,也就是只要姓名、年齡相同的學生就認定為信息重復,即使學號不同。

解法二

上面的解法一到了這個問題就失效了,對於這個問題我只能想到用下面的代碼來解決

for (int i = 0; i < repo3.size(); i++)
        {
            T3.Student stu = repo3.get(i);//Lambda 表達式不允許我用沒被引用的變量,所以就把這句單獨提了出來
            repo3.removeIf(s->s.equals(stu));//根據題意定義的 equals 方法
        }

增加了一個 for 循環,其余的基本沒變,但是代碼的簡潔程度相較於使用迭代器得到了大幅度提高。


踩到的坑

0.為毛不用foreach或者forEach循環?

foreach不能適應動態變化的集合,因為我在動作中刪除了元素。
forEach雖然是一個內部循環,有並行計算的優勢,但是還是由於上面的原因不能使用。

1.Stream 接口的操作不會對原有的數據產生影響。

repo3.removeAll(repo3.stream()
     .filter(
             s -> repo3.stream().filter(stu -> stu.equals(s)).count() != 0)
     .collect(Collectors.toList()));

本來我是想用這種方法拿到所有重復的元素,然而事實上是不行的,因為Stream 接口的操作不會對原有的數據產生影響。導致第二個 filter 會把所有元素重新掃描一遍,所以需要改成下面的代碼:

repo3.removeIf
        (
                s -> repo3
                .subList(repo3.indexOf(s),repo3.size())
                .stream()
                .filter(stu -> stu.equals(s))
                .count() != 0
        );

看起來好像比解法二復雜了許多但是在效率上有很大的進步,Stream API是並行化的,比外部循環不知道要高到哪里去了,然而這里還存在一個問題,就是在subList中獲得對當前元素的索引的速度可能會拖慢效率,然而,在這道題目的測試用例當中學號起到了索引的作用,然后這里的效率可以大幅度提升。

總結

一開始准備用 Stream API 我是拒絕的,我看到它是以方法的形式出現的,我還以為會出現類型轉換,后來發現這是 Java 缺少 extend methods 才出現的東西。然后這個東西實現了跟 Linq 差不多的功能,配合 Lambda 表達式很好用。
那么下面給出最終的版本:

repo3.removeIf
                (
                        s -> repo3
                        .stream()
                        .filter(stu -> stu.equals(s))
                        .count() != 0
                );

這里之所以不需要把 list 截斷可能是因為 removeif也是一個stream方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM