Articles

リストによるJavaコレクションのフィルタリング

概要

リストによるコレクションのフィルタリングは、一般的なビジネスロジックのシナリオです。 これを達成する方法はたくさんあります。 しかし、適切に行われていない場合は、実行不足の解決策につながる可能性があります。

このチュートリアルでは、いくつかのフィルタリング実装を比較し、その利点と欠点について説明します。

For-Eachループの使用

最も古典的な構文であるfor-eachループから始めます。

この例とこの記事の他のすべての例では、次のクラスを使用します。

public class Employee { private Integer employeeNumber; private String name; private Integer departmentId; //Standard constructor, getters and setters.}

簡単にするために、すべての例で次のメソッドを使用します。

private List<Employee> buildEmployeeList() { return Arrays.asList( new Employee(1, "Mike", 1), new Employee(2, "John", 1), new Employee(3, "Mary", 1), new Employee(4, "Joe", 2), new Employee(5, "Nicole", 2), new Employee(6, "Alice", 2), new Employee(7, "Bob", 3), new Employee(8, "Scarlett", 3));}private List<String> employeeNameFilter() { return Arrays.asList("Alice", "Mike", "Bob");}

この例では、従業員名を持つ第二のリストに基づいて最初の従業員のリストをフィルタリングし、それらの特定の名前を持つ従業員のみを検索します。

今、のは、伝統的なアプローチを見てみましょう–一致を探して、両方のリストをループします:これは単純な構文ですが、非常に冗長で、実際には非常に非効率的です。 簡単に言えば、それは私たちの答えを得るために2つのセットのデカルト積を反復します。

早期に終了するために休憩を追加しても、平均的なケースではデカルト積と同じ順序で反復されます。従業員リストnのサイズを呼び出すと、nameFilterは同じ大きさの順序になり、O(n2)分類が与えられます。

StreamsとList#containsを使用して

構文を簡素化し、読みやすさを向上させるためにラムダを使用して、前のメソッドをリファクタリングします。 また、list#containsメソッドをラムダフィルタとして使用してみましょう。

@Testpublic void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambda() { List<Employee> filteredList; List<Employee> originalList = buildEmployeeList(); List<String> nameFilter = employeeNameFilter(); filteredList = originalList.stream() .filter(employee -> nameFilter.contains(employee.getName())) .collect(Collectors.toList()); assertThat(filteredList.size(), is(nameFilter.size()));}

Stream APIを使用することで、可読性が大幅に向上しましたが、デカルト積を内部的に反復処理しているため、コードは以前のメソッドと同じくらい非効率的です。 したがって、我々は同じO(n2)分類を持っています。

Hashsetでストリームを使用する

パフォーマンスを向上させるには、HashSet#containsメソッドを使用する必要があります。 このメソッドはList#containsとは異なり、ハッシュコードの検索を実行し、一定時間の操作数を与えるためです。

@Testpublic void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambdaAndHashSet() { List<Employee> filteredList; List<Employee> originalList = buildEmployeeList(); Set<String> nameFilterSet = employeeNameFilter().stream().collect(Collectors.toSet()); filteredList = originalList.stream() .filter(employee -> nameFilterSet.contains(employee.getName())) .collect(Collectors.toList()); assertThat(filteredList.size(), is(nameFilterSet.size()));}

HashSetを使用することにより、コード効率が大幅に改善されましたが、読みやすさには影響しませんでした。 HashSet#には一定の時間で実行されるため、分類をO(n)に改善しました。

結論

このクイックチュートリアルでは、値のリストでコレクションをフィルタリングする方法と、最も簡単な方法のように見えるかもしれ

コードは巨大なデータセットで実行される可能性があり、パフォーマンスの問題はそのような環境で壊滅的な結果をもたらす可能性があるため、常に効率性を考慮する必要があります。

この記事で紹介されているすべてのコードはGitHubで入手できます。>>>コースをチェックしてください