Java List<T> 엘리먼트 특정 속성값 기반 중복 제거하기
Distinct List by Object Property in Java
Java Functional 인터페이스 및 Stream API를 활용하여 특정 속성을 기준으로 List<T>
의 중복 엘리먼트를 제거하는 방법입니다.
Implementing
먼저, Stream API의 filter()
에서 활용할 Predicate<? super T>
을 구현합니다. List<T>
엘리먼트를 순회하면서 기준이 되는 속성 값이 이전에 없었는지 판별하는 로직입니다.
private static <T> Predicate<T> isUniqueKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
Predicate<T>
: T 타입의 인자를 받아boolean
타입을 반환하는 함수형 인터페이스입니다.Function<? super T, ?>
: T 타입의 인자를 받아 R 타입으로 반환하는 함수형 인터페이스입니다.ConcurrentHashMap
: 멀티 쓰레드 환경에서 보다 thread-safe한Map<K, V>
의 확장 클래스입니다.V putIfAbsent(K key, V value)
: 동일한 Key가 없는 경우null
을 반환합니다. 이를 통해 해당 Key의 중복 여부를 판별할 수 있습니다.
위에서 정의한 함수를 활용하여 중복이 제거된 새로운 List<T>
를 반환하는 로직을 완성합니다.
public static List<E> distinctByKey(List<E> elements) {
return elements.stream()
.filter(isUniqueKey(E::getKey))
.toList();
}
Playgrounds
테스트 코드를 통해 중복 필터링 로직이 기대한 대로 동작하는지 확인합니다.
DistinctTest.java
@Test
void shouldDistinct() throws Exception {
// Given
String phone = "010-1234-5678";
Cat catA = new Cat("catA", phone);
Cat catB = new Cat("catB", phone);
List<Cat> cats = List.of(catA, catB);
// When
List<Cat> uniques = distinctByKey(cats);
// Then
assertThat(cats).hasSize(2);
assertThat(uniques).hasSize(1);
assertThat(uniques.contains(catA)).isTrue();
assertThat(uniques.contains(catB)).isFalse();
}
public static List<Cat> distinctByKey(List<Cat> cats) {
return cats.stream()
.filter(isUniqueKey(Cat::getPhone))
.collect(Collectors.toList());
}
private static <T> Predicate<T> isUniqueKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> {
Object apply = keyExtractor.apply(t);
boolean b = seen.putIfAbsent(apply, Boolean.TRUE) == null;
return b;
};
}
Cat.java
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Cat {
private String name;
private String phone;
}
인스턴스의 phone
속성을 기반으로 중복된 엘리먼트가 없는 새로운 List<Cat>
가 반환되었습니다. Stream API는 순차적으로 순회하기 때문에 먼저 추가된 catA
인스턴스만 필터를 통과하였습니다.
- Java