いくつかのプロパティに基づいてオブジェクトのリストから重複を削除しようとしています。
Java 8を使用して簡単な方法でそれを行うことができます
List<Employee> employee
従業員のid
プロパティに基づいて重複を削除できますか。文字列の配列リストから重複文字列を削除する投稿を見てきました。
List
からストリームを取得し、TreeSet
に入れると、IDを一意に比較するカスタムコンパレータを提供できます。
次に、本当にリストが必要な場合は、このコレクションをArrayListに戻します。
import static Java.util.Comparator.comparingInt;
import static Java.util.stream.Collectors.collectingAndThen;
import static Java.util.stream.Collectors.toCollection;
...
List<Employee> unique = employee.stream()
.collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparingInt(Employee::getId))),
ArrayList::new));
例を挙げます:
List<Employee> employee = Arrays.asList(new Employee(1, "John"), new Employee(1, "Bob"), new Employee(2, "Alice"));
出力されます:
[Employee{id=1, name='John'}, Employee{id=2, name='Alice'}]
別のアイデアは、従業員をラップするラッパーを使用し、そのidに基づいてequalsおよびhashcodeメソッドを使用することです。
class WrapperEmployee {
private Employee e;
public WrapperEmployee(Employee e) {
this.e = e;
}
public Employee unwrap() {
return this.e;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WrapperEmployee that = (WrapperEmployee) o;
return Objects.equals(e.getId(), that.e.getId());
}
@Override
public int hashCode() {
return Objects.hash(e.getId());
}
}
次に、各インスタンスをラップし、distinct()
を呼び出し、それらをアンラップして、結果をリストに収集します。
List<Employee> unique = employee.stream()
.map(WrapperEmployee::new)
.distinct()
.map(WrapperEmployee::unwrap)
.collect(Collectors.toList());
実際、比較を行う関数を提供することで、このラッパーをジェネリックにできると思います。
class Wrapper<T, U> {
private T t;
private Function<T, U> equalityFunction;
public Wrapper(T t, Function<T, U> equalityFunction) {
this.t = t;
this.equalityFunction = equalityFunction;
}
public T unwrap() {
return this.t;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
Wrapper<T, U> that = (Wrapper<T, U>) o;
return Objects.equals(equalityFunction.apply(this.t), that.equalityFunction.apply(that.t));
}
@Override
public int hashCode() {
return Objects.hash(equalityFunction.apply(this.t));
}
}
マッピングは次のようになります。
.map(e -> new Wrapper<>(e, Employee::getId))
リストで直接行う最も簡単な方法は
HashSet<Object> seen=new HashSet<>();
employee.removeIf(e->!seen.add(e.getID()));
removeIf
は、指定された基準を満たす場合に要素を削除しますSet.add
は、false
を変更しなかった場合、つまり既に値が含まれている場合はSet
を返しますもちろん、リストが要素の削除をサポートしている場合にのみ機能します。
このコードを試してください:
Collection<Employee> nonDuplicatedEmployees = employees.stream()
.<Map<Integer, Employee>> collect(HashMap::new,(m,e)->m.put(e.getId(), e), Map::putAll)
.values();
equals
を使用できる場合は、ストリーム内でdistinct
を使用してリストをフィルターします(上記の回答を参照)。 equals
メソッドをオーバーライドできない場合、またはオーバーライドしたくない場合は、任意のプロパティに対して次の方法でストリームをfilter
できます。プロパティ名の場合(プロパティIDなども同じ):
Set<String> nameSet = new HashSet<>();
List<Employee> employeesDistinctByName = employees.stream()
.filter(e -> nameSet.add(e.getName()))
.collect(Collectors.toList());
順序が重要でなく、並行して実行するほうがパフォーマンスが良い場合は、マップに収集してから値を取得します。
employee.stream().collect(Collectors.toConcurrentMap(Employee::getId, Function.identity(), (p, q) -> p)).values()
これは私のために働いた:
list.stream().distinct().collect(Collectors.toList());
もちろん、equalsを実装する必要があります
別の解決策は、述語を使用することです。その後、任意のフィルターでこれを使用できます。
public static <T> Predicate<T> distinctBy(Function<? super T, ?> f) {
Set<Object> objects = new ConcurrentHashSet<>();
return t -> objects.add(f.apply(t));
}
その後、単純にどこでも述語を再利用します:
employees.stream().filter(distinctBy(e -> e.getId));
注:JavaDocのフィルターでは、ステートレスのPredicteを使用すると言われています。実際、これはストリームが並列であっても正常に機能します。
他のソリューションについて:
1).collect(Collectors.toConcurrentMap(..)).values()
を使用するのは良い解決策ですが、並べ替えて順序を維持するのは面倒です。
2)stream.removeIf(e->!seen.add(e.getID()));
も別の非常に優れたソリューションです。ただし、コレクションにremoveIfが実装されていることを確認する必要があります。たとえば、Arrays.asList(..)
を使用してコレクションを構築すると例外がスローされます。
ここには多くの良い答えがありますが、reduce
メソッドの使用に関するものは見つかりませんでした。したがって、あなたの場合、次の方法で適用できます。
List<Employee> employeeList = employees.stream()
.reduce(new ArrayList<>(), (List<Employee> accumulator, Employee employee) ->
{
if (accumulator.stream().noneMatch(emp -> emp.getId().equals(employee.getId())))
{
accumulator.add(employee);
}
return accumulator;
}, (acc1, acc2) ->
{
acc1.addAll(acc2);
return acc1;
});
シンプルな別のバージョン
BiFunction<TreeSet<Employee>,List<Employee> ,TreeSet<Employee>> appendTree = (y,x) -> (y.addAll(x))? y:y;
TreeSet<Employee> outputList = appendTree.apply(new TreeSet<Employee>(Comparator.comparing(p->p.getId())),personList);