java

[Java8] 중복 데이터 제거를 위한 Stream distinct

moonsiri 2020. 10. 31. 02:36
728x90
반응형

중복 데이터를 제거하기 위해 Stream distinct를 사용합니다.

동일한 객체의 판단하는 기준은 Object.equals(Object)의 결과 값이다. String 객체의 경우 equals()가 이미 구현되어 있습니다.

- Stream<T>.distinct() : Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.

 

List<String> strings = Arrays.asList("hello", "world", "hello", "java", "hello");

strings.stream().distinct().forEach(System.out::println);  // "hello", "world", "java"

 

 


 

 

만약 String객체가 아닌 VO를 사용한다면, equals, hashCode가 재정의 되어야 합니다.

- Object.equals(Objecr) : Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method​, which states that equal objects must have equal hash codes.

 

List<StringVO> stringVOList = Arrays.asList(new StringVO("hello"), new StringVO("world"), new StringVO("hello"), new StringVO("java"), new StringVO("hello"));

stringVOList.stream().distinct().forEach(stringVO -> System.out.println(stringVO.getStr()));
public class StringVO {
    public String str;

    public StringVO(String str) {
        this.str = str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public String getStr() {
        return str;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof StringVO) {
            return this.str.equals(((StringVO) o).getStr());   // 값 비교
        }
        return false;
    }

    @Override
    public int hashCode() {
        return str.hashCode();
    }
}

 

Lombok을 사용한다면, @EqualsAndHashCode 어노테이션을 사용하여 재정의할 수 있습니다.

 

 


 

 

equals, hashCode를 재정의하지 않고 사용하는 방법은 다음과 같습니다.

 

/**
* Stream에서 distinct 다중키 비교를 위함 (hashCode, equals 재정의를 하지 않고)
*  - 예) list.stream().filter(distinctByKeys(UserVO::getName, UserVO::getAge)).distinct()...
*
* @param keyExtractors Object[]
* @return Predicate
*/
public static <T> Predicate<T> distinctByKeys(Function<? super T, ?>... keyExtractors) {
    final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();

    return t -> {
        final List<?> keys = Arrays.stream(keyExtractors).map(ke -> ke.apply(t)).collect(Collectors.toList());

        return seen.putIfAbsent(keys, Boolean.TRUE) == null;
    };
}
List<StringVO> stringVOList = Arrays.asList(new StringVO("hello"), new StringVO("world"), new StringVO("hello"), new StringVO("java"), new StringVO("hello"));

stringVOList.stream().filter(distinctByKeys(StringVO::getStr)).distinct().forEach(stringVO -> System.out.println(stringVO.getStr()));

 

 

 

 

[Reference]

https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/stream/Stream.html

https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Object.html

728x90
반응형