HOWTO · Java

Java 8 ストリームのプロパティによる区別

このチュートリアルでは、Java でストリームを使用してプロパティごとに異なる機能を示します。

このページの内容

このチュートリアルでは、Java でストリームを使用してプロパティごとに異なる機能を示します。

Java 8 ストリームのプロパティによる区別

Java 8 Stream には、重複をリストから除外するメソッド distinct() があります。 このメソッドは、インスタンスの equal メソッドを使用して一意の要素をチェックし、重複が見つかった場合はそれらを削除します。

特定のプロパティまたはフィールドに適用する必要がある場合、distinct() メソッドは使用できません。 しかし、特定の方法を使用して、プロパティごとに distinct() メソッドを適用できます。

従業員の名前と ID を持つクラス Employee を作成し、Employee クラス リストのストリームで distinct() メソッドを使用する例を試してみましょう。

package delftstack;

import java.util.List;
import java.util.Objects;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));
    // the distinct() will remove the duplicates by equals
    Employee_List.stream().distinct().forEach(System.out::println);
  }
}

上記のコードは、ストリームを使用して Employee クラスのリストで distinct() メソッドを使用し、同じ値を持つエントリを削除します。 このコードは、distinct() メソッドの簡単な使用法を示しています。

それでは、Java でプロパティごとに異なるロジックを適用する方法を見てみましょう。

Collectors.toMap を使用して Java で Distinct by Property を適用する

Collectors.toMap を使用して、ストリームの要素をマップに収集し、プロパティまたはフィールドによってキー設定することができます。 マップは 1つのキーに対して 1つの値しか持てないため、キーごとに最初のストリーム オブジェクトを選択する必要があります。

結果のマップから values() を呼び出すことができます。これにより、個別のロジックが適用され、グループ化したカスタム プロパティによって一意の値が得られます。 例を見てみましょう:

package delftstack;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));

    Collection<Employee> Unique_EmployeeList =
        Employee_List.stream()
            .collect(Collectors.toMap(
                Employee::getName, Function.identity(), (Employee1, Employee2) -> Employee1))
            .values();
    System.out.println(Unique_EmployeeList);
  }
}

上記のコードは、Employee_Name プロパティに基づいて Collection.toMap メソッドを使用してストリームに個別のロジックを適用します。これは、コードが一意の従業員名のリストを返すことを意味します。 出力を参照してください:

[Jake 50, Logan 60, Robert 40, John 20, Sheeraz 10]

ご覧のとおり、Employee リストには Sheeraz という名前のエントリが 2つあり、name プロパティに基づいて個別に適用したため、そのうちの 1つが削除されています。

ラッパー クラスを使用して Java で Distinct by プロパティを適用する

ラッパー クラスを作成し、Employee クラスにプロパティを割り当ててから、ラッパー クラスのプロパティごとに distinct() メソッドを適用することもできます。 ラッパー クラスはプライベートな静的であり、ドライバー クラスで宣言されます。

例を参照してください:

package delftstack;

import java.util.List;
import java.util.Objects;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  private static class EmployeeWrapper {
    private Employee Employee;
    private EmployeeWrapper(Employee Employee) {
      this.Employee = Employee;
    }
    public Employee getEmployee() {
      return Employee;
    }
    @Override
    public boolean equals(Object Demo_Object) {
      if (this == Demo_Object) {
        return true;
      }
      if (Demo_Object == null) {
        return false;
      }
      if (getClass() != Demo_Object.getClass()) {
        return false;
      }
      EmployeeWrapper other = (EmployeeWrapper) Demo_Object;
      return Objects.equals(Employee.getName(), other.Employee.getName());
    }
    @Override
    public int hashCode() {
      return Objects.hash(Employee.getName());
    }
  }

  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));

    Employee_List.stream()
        .map(EmployeeWrapper::new)
        .distinct()
        .map(EmployeeWrapper::getEmployee)
        .forEach(System.out::println);
  }
}

上記のコードは、ストリームからの distinct() メソッドでラッパー クラス EmployeeWrapper を使用して、一意の名前を持つ従業員のリストを取得します。 ラッパー クラスでは、equals() メソッドでプロパティ名を割り当て、それをドライバー クラスで使用していることがわかります。

出力を参照してください。

Sheeraz 10
John 20
Robert 40
Jake 50
Logan 60

distinctByKey メソッドを使用して Java で Distinct by プロパティを適用する

この方法では、同時ハッシュ マップを使用して、同じ値を持つ既存のキーがあるかどうかを確認する必要があります。 このメソッドは、ドライバー クラスで宣言され、filter() メソッドを使用してストリームで使用されます。

例を参照してください。

package delftstack;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

class Employee {
  private String Employee_Name;
  private int Employee_ID;

  public Employee(String Employee_Name, int Employee_ID) {
    this.Employee_Name = Employee_Name;
    this.Employee_ID = Employee_ID;
  }

  public String getName() {
    return Employee_Name;
  }

  public int getID() {
    return Employee_ID;
  }

  @Override
  public boolean equals(Object Demo_Object) {
    if (this == Demo_Object) {
      return true;
    }
    if (Demo_Object == null) {
      return false;
    }
    if (getClass() != Demo_Object.getClass()) {
      return false;
    }
    Employee other = (Employee) Demo_Object;
    return Objects.equals(Employee_Name, other.Employee_Name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(Employee_Name);
  }

  @Override
  public String toString() {
    return Employee_Name + " " + Employee_ID;
  }
}

public class Example {
  public static <T> Predicate<T> distinctByKey(Function<? super T, Object> Key_Extractor) {
    Map<Object, Boolean> Employee_Map = new ConcurrentHashMap<>();
    return t -> Employee_Map.putIfAbsent(Key_Extractor.apply(t), Boolean.TRUE) == null;
  }

  public static void main(String[] args) {
    List<Employee> Employee_List =
        List.of(new Employee("Sheeraz", 10), new Employee("John", 20), new Employee("Sheeraz", 30),
            new Employee("Robert", 40), new Employee("Jake", 50), new Employee("Logan", 60));

    // Get distinct objects by one key
    List<Employee> Distinct_Employees1 =
        Employee_List.stream().filter(distinctByKey(p -> p.getName())).collect(Collectors.toList());
    System.out.println(Distinct_Employees1);

    // Get distinct objects by one key
    List<Employee> Distinct_Employees2 =
        Employee_List.stream()
            .filter(distinctByKey(p -> p.getName() + " " + p.getID()))
            .collect(Collectors.toList());
    System.out.println(Distinct_Employees2);
  }
}

上記のコードは、名前に基づいて、また名前と ID に基づいて個別の要素を取得しようとします。 出力を参照してください。

[Sheeraz 10, John 20, Robert 40, Jake 50, Logan 60]
[Sheeraz 10, John 20, Sheeraz 30, Robert 40, Jake 50, Logan 60]

ご覧のとおり、最初のリストは name プロパティのみで区別され、2 番目のリストは name プロパティと ID プロパティの両方で区別されます。