Java 8 Stream
에서 각 객체의 속성이 다른지 확인하여 API를 사용하여 컬렉션을 필터링하려면 어떻게해야합니까?
예를 들어 Person
객체 목록이 있고 같은 이름을 가진 사람을 삭제하고 싶습니다.
persons.stream().distinct();
Person
객체에 기본 평등 검사를 사용 하므로 다음과 같은 것이 필요합니다.
persons.stream().distinct(p -> p.getName());
불행히도이 distinct()
방법에는 그러한 과부하가 없습니다. Person
클래스 내부의 동등성 검사를 수정하지 않고 간결하게 수행 할 수 있습니까?
답변
고려 distinct
로 상태 필터 . 다음은 이전에 본 것에 대한 상태를 유지하고 주어진 요소가 처음으로 보이는지 여부를 리턴하는 술어를 리턴하는 함수입니다.
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
그럼 당신은 쓸 수 있습니다 :
persons.stream().filter(distinctByKey(Person::getName))
스트림이 정렬되고 병렬로 실행되는 경우 첫 번째 항목 대신 중복 요소 중 임의의 요소가 다음과 같이 유지됩니다.distinct()
.
(이것은 본질적 으로이 질문에 대한 나의 대답 과 동일합니다 : 임의의 키에 대한 Java Lambda Stream Distinct ()? )
답변
대안은 이름을 키로 사용하여 사람을지도에 배치하는 것입니다.
persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();
이름이 중복되는 경우 보관 된 사람이 첫 번째로 확인됩니다.
답변
개인 오브젝트를 다른 클래스로 랩핑하여 개인의 이름 만 비교할 수 있습니다. 그런 다음 랩핑 된 오브젝트의 랩을 해제하여 사람 스트림을 다시 가져 오십시오. 스트림 작업은 다음과 같습니다.
persons.stream()
.map(Wrapper::new)
.distinct()
.map(Wrapper::unwrap)
...;
수업 Wrapper
은 다음과 같습니다.
class Wrapper {
private final Person person;
public Wrapper(Person person) {
this.person = person;
}
public Person unwrap() {
return person;
}
public boolean equals(Object other) {
if (other instanceof Wrapper) {
return ((Wrapper) other).person.getName().equals(person.getName());
} else {
return false;
}
}
public int hashCode() {
return person.getName().hashCode();
}
}
답변
를 사용하는 다른 솔루션 Set
. 이상적인 솔루션은 아니지만 작동합니다.
Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());
또는 원래 목록을 수정할 수있는 경우 removeIf 메소드를 사용할 수 있습니다
persons.removeIf(p -> !set.add(p.getName()));
답변
커스텀 비교기와 함께 TreeSet을 사용하는 더 간단한 방법이 있습니다.
persons.stream()
.collect(Collectors.toCollection(
() -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName()))
));
답변
RxJava (매우 강력한 반응성 확장 라이브러리)를 사용할 수도 있습니다.
Observable.from(persons).distinct(Person::getName)
또는
Observable.from(persons).distinct(p -> p.getName())
답변
groupingBy
수집기 를 사용할 수 있습니다 .
persons.collect(Collectors.groupingBy(p -> p.getName())).values().forEach(t -> System.out.println(t.get(0).getId()));
다른 스트림을 원하면 다음을 사용할 수 있습니다.
persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream().map(l -> (l.get(0)));