본문 바로가기
java

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

by moonsiri 2020. 10. 31.
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
반응형

댓글