Java 8의 속성을 기반으로 객체 목록에서 중복 제거
이 질문에 이미 답변이 있습니다.
- Java 8 속성 별 구별 23 답변
일부 속성을 기반으로 개체 목록에서 중복을 제거하려고합니다.
Java 8을 사용하여 간단한 방법으로 할 수 있습니까?
List<Employee> employee
id
직원의 재산에 따라 중복을 제거 할 수 있습니까 ? 중복 문자열을 문자열의 arraylist에서 제거하는 게시물을 보았습니다.
에서 스트림을 가져 와서 ID를 고유하게 비교하는 사용자 지정 비교기를 제공하는 List
에 넣을 수 있습니다 TreeSet
.
그런 다음 목록이 정말로 필요하면이 컬렉션을 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
. 즉 이미 값이 포함되어 있습니다.- 이 두 가지를 결합하면 이전에 ID가 발생한 모든 요소 (직원)가 제거됩니다.
물론 목록이 요소 제거를 지원하는 경우에만 작동합니다.
이 코드를 시도하십시오.
Collection<Employee> nonDuplicatedEmployees = employees.stream()
.<Map<Integer, Employee>> collect(HashMap::new,(m,e)->m.put(e.getId(), e), Map::putAll)
.values();
If you can make use of equals
, then filter the list by using distinct
within a stream (see answers above). If you can not or don't want to override the equals
method, you can filter
the stream in the following way for any property, e.g. for the property Name (the same for the property Id etc.):
Set<String> nameSet = new HashSet<>();
List<Employee> employeesDistinctByName = employees.stream()
.filter(e -> nameSet.add(e.getName()))
.collect(Collectors.toList());
If order does not matter and when it's more performant to run in parallel, Collect to a Map and then get values:
employee.stream().collect(Collectors.toConcurrentMap(Employee::getId, Function.identity(), (p, q) -> p)).values()
This worked for me:
list.stream().distinct().collect(Collectors.toList());
You need to implement equals, of course
Another solution is to use a Predicate, then you can use this in any filter:
public static <T> Predicate<T> distinctBy(Function<? super T, ?> f) {
Set<Object> objects = new ConcurrentHashSet<>();
return t -> objects.add(f.apply(t));
}
Then simply reuse the predicate anywhere:
employees.stream().filter(distinctBy(e -> e.getId));
Note: in the JavaDoc of filter, which says it takes a stateless Predicte. Actually, this works fine even if the stream is parallel.
About other solutions:
1) Using .collect(Collectors.toConcurrentMap(..)).values()
is a good solution, but it's annoying if you want to sort and keep the order.
2) stream.removeIf(e->!seen.add(e.getID()));
is also another very good solution. But we need to make sure the collection implemented removeIf, for example it will throw exception if we construct the collection use Arrays.asList(..)
.
Another version which is simple
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);
There are a lot of good answers here but I didn't find the one about using reduce
method. So for your case, you can apply it in following way:
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;
});
'Programing' 카테고리의 다른 글
리디렉션시 Rails 플래시 알림을 표시하는 방법은 무엇입니까? (0) | 2020.12.14 |
---|---|
위조 방지 토큰을 해독 할 수 없습니다. (0) | 2020.12.14 |
Webpack "OTS 구문 분석 오류"글꼴로드 (0) | 2020.12.14 |
ASP.NET Core에서 소문자 라우팅을 어떻게 적용하나요? (0) | 2020.12.14 |
사용자 정의 유형이있는 std :: maps를 키로 사용하려면 어떻게해야합니까? (0) | 2020.12.14 |