속성 별 Java 8 구별


455

Java 8 Stream에서 각 객체의 속성이 다른지 확인하여 API를 사용하여 컬렉션을 필터링하려면 어떻게해야합니까?

예를 들어 Person객체 목록이 있고 같은 이름을 가진 사람을 삭제하고 싶습니다.

persons.stream().distinct();

Person객체에 기본 평등 검사를 사용 하므로 다음과 같은 것이 필요합니다.

persons.stream().distinct(p -> p.getName());

불행히도이 distinct()방법에는 그러한 과부하가 없습니다. Person클래스 내부의 동등성 검사를 수정하지 않고 간결하게 수행 할 수 있습니까?

답변:


556

고려 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 ()? )


27
더 나은 호환성을 위해서는 논쟁이 Function<? super T, ?>아니라고 생각 Function<? super T, Object>합니다. 또한 정렬 된 병렬 스트림의 경우이 솔루션은 (일반과 달리 distinct()) 추출 될 객체를 보장하지 않습니다 . 또한 순차 스트림의 경우 CHM 사용에 대한 추가 오버 헤드가 있습니다 (@nosid 솔루션에는 없음). 마지막으로이 솔루션은 filterJavaDoc에 명시된대로 술어가 상태 비 저장이어야하는 메소드 계약을 위반합니다 . 그럼에도 불구하고 공감했다.
Tagir Valeev

3
@java_newbie에 의해 반환 된 Predicate 인스턴스는 distinctByKey그것이 병렬 스트림 내에서 사용되고 있는지 전혀 모른다. Tagir Valeev가 위에서 언급 한 것처럼 순차적 인 경우 오버 헤드를 추가하지만 CHM을 병렬로 사용하는 경우 CHM을 사용합니다.
스튜어트 마크

5
@holandaGo에서 반환 한 Predicate 인스턴스를 저장하고 재사용하면 실패합니다 distinctByKey. 그러나 distinctByKey매번 호출하면 매번 새로운 Predicate 인스턴스를 생성하도록 작동합니다 .
Stuart Marks

3
@Chinmay 아니오, 그렇게해서는 안됩니다. 를 사용하는 경우 .filter(distinctByKey(...)). 메소드를 한 번 실행하고 술어를 리턴합니다. 따라서 기본적으로지도를 스트림 내에서 올바르게 사용하면 이미 재사용되고 있습니다. 맵을 정적으로 만들면 모든 용도로 맵이 공유됩니다. 따라서 this distinctByKey()을 사용하는 두 개의 스트림이있는 경우 둘 다 동일한 맵을 사용합니다. 원하는 것은 아닙니다.
g00glen00b

3
이것은 똑똑하고 완전히 명백합니다. 일반적으로 이것은 상태 저장 람다이며 기본 CallSiteget$Lambda메소드에 연결되어 항상 새로운 인스턴스를 반환 Predicate하지만 해당 인스턴스는 내가 이해 하는 한 동일 map하고 공유됩니다 function. 아주 좋아요!
유진

152

대안은 이름을 키로 사용하여 사람을지도에 배치하는 것입니다.

persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();

이름이 중복되는 경우 보관 된 사람이 첫 번째로 확인됩니다.


23
@ skiwi : distinct()오버 헤드없이 구현하는 방법이 있다고 생각 하십니까? 실제로 보이는 모든 고유 값을 기억하지 않고 객체를 본 적이 있는지 어떤 구현이 어떻게 알 수 있습니까? 따라서 오버 헤드 toMap와 오버 헤드 distinct는 거의 동일합니다.
Holger

1
@Holger 오버 헤드 distinct()자체가 부끄러워한다고 생각하지 않아서 잘못되었을 수 있습니다 .
skiwi

2
그리고 그것은 분명히 목록의 원래 순서
Philipp

10
@Philipp : 다음으로 변경하여 수정 가능persons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
Holger

1
@DanielEarwicker이 질문은 "속성 별"에 관한 것입니다. 스트림을 활용 하려면 동일한 속성 으로 스트림을 정렬 해야합니다. 첫째, OP는 스트림이 전혀 정렬되지 않았다고 명시하지 않았습니다. 둘째, 스트림은 특정 속성 별로 정렬 되어 있는지 여부를 감지 할 수 없습니다 . 셋째, 제안한 것을 수행하기위한 진정한 "속성 별"스트림 작업은 없습니다. 넷째, 실제로 이러한 정렬 된 스트림을 얻는 방법은 두 가지뿐입니다. TreeSet어쨌든 이미 sorted스트림에서 구별되는 정렬 된 소스 ( )는 모든 요소를 ​​버퍼링합니다.
Holger

101

개인 오브젝트를 다른 클래스로 랩핑하여 개인의 이름 만 비교할 수 있습니다. 그런 다음 랩핑 된 오브젝트의 랩을 해제하여 사람 스트림을 다시 가져 오십시오. 스트림 작업은 다음과 같습니다.

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();
    }
}


5
@StuartCaie 실제로는 ... 메모가 없으며, 요점은 성능이 아니라 기존 API에 대한 적응입니다.
Marko Topolnik

6
com.google.common.base.Equivalence.wrap (S) 및 com.google.common.base.Equivalence.Wrapper.get ()도 도움이 될 수 있습니다.
bjmi

랩퍼 클래스를 키 추출 함수로 일반화하고 매개 변수화 할 수 있습니다.
Lii

equals방법을 간략화 할 수있다return other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
홀가

55

를 사용하는 다른 솔루션 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()));

2
타사 라이브러리를 사용하지 않는 경우 가장 좋은 방법입니다!
Manoj Shrestha

5
Set.add가 이미 지정된 요소를 포함하고 있지 않은 경우, Set.add가 true를 리턴한다고 생각하는 것을 사용합니다. +1
Luvie

이 방법은 스레드 안전하지 않기 때문에 병렬 스트림 처리에는 작동하지 않는다고 생각합니다.
LoBo

@LoBo 아마 아닙니다. 이것은 단순한 경우에 효과가있는 아이디어 일뿐입니다. 사용자는 나사산 안전 / 평 행성을 위해이를 확장 할 수 있습니다.
Santhosh

흥미로운 접근 방식이지만 다른 컬렉션 (사람)의 스트림을 필터링하는 동안 외부 컬렉션 (세트)을 수정하는 반 패턴처럼 보입니다.
Justin Rowe

31

커스텀 비교기와 함께 TreeSet을 사용하는 더 간단한 방법이 있습니다.

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName())) 
));

4
나는 당신의 대답이 독창성이 아닌 질서에 도움이된다고 생각합니다. 그러나 그것은 내가 그것을하는 방법에 대한 내 생각을 세우는 데 도움이되었습니다. 여기를 확인하십시오 : stackoverflow.com/questions/1019854/…
janagn

여기에서 요소를 정렬하는 데 드는 비용을 지불해야하며 중복 항목을 찾거나 중복 항목을 제거하기 위해 정렬 할 필요가 없습니다.
pisaruk

12
Comparator.comparing (Person :: getName)
Jean-François Savard 2016 년

24

RxJava (매우 강력한 반응성 확장 라이브러리)를 사용할 수도 있습니다.

Observable.from(persons).distinct(Person::getName)

또는

Observable.from(persons).distinct(p -> p.getName())

Rx는 대단하지만, 이것은 정답이 아닙니다. 풀 Observable기반이지만 푸시 Stream기반입니다.stackoverflow.com/questions/30216979/…
sdgfsdh

4
이 질문은 반드시 스트림을 사용하지 않는 java8 솔루션을 요구합니다. 내 대답은 java8 스트림 API가 rx api보다 덜 강력하다는 것을 보여줍니다.
frhack

1
사용 원자로를 , 그것은 것입니다Flux.fromIterable(persons).distinct(p -> p.getName())
Ritesh

질문은 말 그대로 " Stream스트림 사용이 아니라" "API 사용"이라고 말합니다 . 즉, 스트림을 별개의 값으로 필터링하는 XY 문제에 대한 훌륭한 솔루션입니다.
M. Justin Justin

12

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)));

11

Eclipse Collections 에서 distinct(HashingStrategy)메소드를 사용할 수 있습니다 .

List<Person> persons = ...;
MutableList<Person> distinct =
    ListIterate.distinct(persons, HashingStrategies.fromFunction(Person::getName));

personsEclipse Collections 인터페이스를 구현하기 위해 리팩터링 할 수있는 경우 목록에서 직접 메소드를 호출 할 수 있습니다.

MutableList<Person> persons = ...;
MutableList<Person> distinct =
    persons.distinct(HashingStrategies.fromFunction(Person::getName));

HashingStrategy 는 단순히 equals 및 hashcode의 사용자 정의 구현을 정의 할 수있는 전략 인터페이스입니다.

public interface HashingStrategy<E>
{
    int computeHashCode(E object);
    boolean equals(E object1, E object2);
}

참고 : 저는 Eclipse Collections의 커미터입니다.


distinctBy 메소드는이 솔루션을 더욱 단순화 할 수있는 Eclipse Collections 9.0에 추가되었습니다. medium.com/@donraab/…
Donald Raab

10

가능 하다면 Vavr을 사용하는 것이 좋습니다 . 이 라이브러리를 사용하면 다음을 수행 할 수 있습니다.

io.vavr.collection.List.ofAll(persons)
                       .distinctBy(Person::getName)
                       .toJavaSet() // or any another Java 8 Collection

이전에는 "javaslang"라이브러리라고했습니다.
user11153

9

StreamEx 라이브러리 를 사용할 수 있습니다 :

StreamEx.of(persons)
        .distinct(Person::getName)
        .toList()

불행히도 멋진 StreamEx 라이브러리의 방법은 제대로 설계되지 않았습니다. 이퀄라이저를 사용하는 대신 객체 동등성을 비교합니다. 이것은 String문자열 인턴 덕분에 작동 하지만 작동 하지 않을 수도 있습니다.
토크

7

Stuart Marks의 답변을 확장하면 병렬 스트림이 필요없는 경우 짧은 시간과 동시지도없이 수행 할 수 있습니다.

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    final Set<Object> seen = new HashSet<>();
    return t -> seen.add(keyExtractor.apply(t));
}

그런 다음 전화 :

persons.stream().filter(distinctByKey(p -> p.getName());

2
이것은 스트림이 병렬 일 수 있다는 점을 고려하지 않습니다.
brunnsbe

의견 주셔서 감사합니다, 나는 내 대답을 업데이트했습니다. 병렬 스트림이 필요하지 않은 경우 동시 맵을 사용하지 않으면 성능이 훨씬 향상됩니다.
Wojciech Górski

Collections.synchronizedSet(new HashSet<>())대신 코드를 만들면 코드가 병렬 컬렉션에서 작동 합니다. 그러나 아마도 a보다 느릴 것입니다 ConcurrentHashMap.
Lii

7

Saeed Zarinfam과 비슷한 접근법이지만 Java 8 스타일이 더 많습니다.)

persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream()
 .map(plans -> plans.stream().findFirst().get())
 .collect(toList());

1
와 나는지도 라인을 대체 할 거라고 flatMap(plans -> plans.stream().findFirst().stream())는 옵션에 GET의 사용을 피한다
앤드류 Sneck에게

어쩌면 이것은 또한 괜찮 : flatMap. (계획 -> plans.stream () 제한 (1))
RRR

6

나는 일반 버전을 만들었습니다.

private <T, R> Collector<T, ?, Stream<T>> distinctByKey(Function<T, R> keyExtractor) {
    return Collectors.collectingAndThen(
            toMap(
                    keyExtractor,
                    t -> t,
                    (t1, t2) -> t1
            ),
            (Map<R, T> map) -> map.values().stream()
    );
}

예 :

Stream.of(new Person("Jean"), 
          new Person("Jean"),
          new Person("Paul")
)
    .filter(...)
    .collect(distinctByKey(Person::getName)) // return a stream of Person with 2 elements, jean and Paul
    .map(...)
    .collect(toList())



5

이것에 대한 나의 접근 방식은 동일한 속성을 가진 모든 객체를 그룹화 한 다음 그룹을 1의 크기로 줄인 다음 마침내로 수집하는 것 List입니다.

  List<YourPersonClass> listWithDistinctPersons =   persons.stream()
            //operators to remove duplicates based on person name
            .collect(Collectors.groupingBy(p -> p.getName()))
            .values()
            .stream()
            //cut short the groups to size of 1
            .flatMap(group -> group.stream().limit(1))
            //collect distinct users as list
            .collect(Collectors.toList());

3

고유 객체 목록은 다음을 사용하여 찾을 수 있습니다.

 List distinctPersons = persons.stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
                            ArrayList::new));

2

이 기능을 구현하는 가장 쉬운 방법은 정렬 기능을 Comparator사용하여 요소의 속성을 사용하여 만들 수 있는 옵션 을 이미 제공하는 것 입니다. 그런 다음 Predicate정렬 된 스트림의 모든 동일한 요소가 인접한다는 사실을 사용하는 statefull 을 사용 하여 수행 할 수있는 중복을 필터링해야합니다 .

Comparator<Person> c=Comparator.comparing(Person::getName);
stream.sorted(c).filter(new Predicate<Person>() {
    Person previous;
    public boolean test(Person p) {
      if(previous!=null && c.compare(previous, p)==0)
        return false;
      previous=p;
      return true;
    }
})./* more stream operations here */;

물론 statefull Predicate은 스레드로부터 안전하지 않지만, 필요한 경우이 논리를 a Collector로 옮기고을 사용할 때 스트림이 스레드 안전을 처리하도록 할 수 있습니다 Collector. 이것은 당신이 당신의 질문에서 우리에게 말하지 않은 독특한 요소의 흐름으로 무엇을하고 싶은지에 달려 있습니다.


1

@ josketres의 답변을 바탕으로 일반적인 유틸리티 방법을 만들었습니다.

Collector 를 생성하여 Java를보다 친숙하게 만들 수 있습니다.

public static <T> Set<T> removeDuplicates(Collection<T> input, Comparator<T> comparer) {
    return input.stream()
            .collect(toCollection(() -> new TreeSet<>(comparer)));
}


@Test
public void removeDuplicatesWithDuplicates() {
    ArrayList<C> input = new ArrayList<>();
    Collections.addAll(input, new C(7), new C(42), new C(42));
    Collection<C> result = removeDuplicates(input, (c1, c2) -> Integer.compare(c1.value, c2.value));
    assertEquals(2, result.size());
    assertTrue(result.stream().anyMatch(c -> c.value == 7));
    assertTrue(result.stream().anyMatch(c -> c.value == 42));
}

@Test
public void removeDuplicatesWithoutDuplicates() {
    ArrayList<C> input = new ArrayList<>();
    Collections.addAll(input, new C(1), new C(2), new C(3));
    Collection<C> result = removeDuplicates(input, (t1, t2) -> Integer.compare(t1.value, t2.value));
    assertEquals(3, result.size());
    assertTrue(result.stream().anyMatch(c -> c.value == 1));
    assertTrue(result.stream().anyMatch(c -> c.value == 2));
    assertTrue(result.stream().anyMatch(c -> c.value == 3));
}

private class C {
    public final int value;

    private C(int value) {
        this.value = value;
    }
}

1

아마도 누군가에게 유용 할 것입니다. 나는 또 다른 요구 사항이 있었다. A타사 의 객체 목록을 사용 하면 동일한 A.b필드 가 동일한 모든 객체 를 제거합니다 A.id( 목록에서 A동일한 여러 객체 A.id). Tagir Valeev의 스트림 파티션 응답은 나에게 custom 을 반환 하도록 영감을주었습니다 . 간단 하게 나머지를 할 것입니다.CollectorMap<A.id, List<A>>flatMap

 public static <T, K, K2> Collector<T, ?, Map<K, List<T>>> groupingDistinctBy(Function<T, K> keyFunction, Function<T, K2> distinctFunction) {
    return groupingBy(keyFunction, Collector.of((Supplier<Map<K2, T>>) HashMap::new,
            (map, error) -> map.putIfAbsent(distinctFunction.apply(error), error),
            (left, right) -> {
                left.putAll(right);
                return left;
            }, map -> new ArrayList<>(map.values()),
            Collector.Characteristics.UNORDERED)); }

1

나는 두 개의 키를 기반으로 목록에서 독특한 요소를 얻는다고 가정 한 상황이있었습니다. 두 개의 키를 기반으로 구별하거나 복합 키를 원한다면 이것을 시도하십시오

class Person{
    int rollno;
    String name;
}
List<Person> personList;


Function<Person, List<Object>> compositeKey = personList->
        Arrays.<Object>asList(personList.getName(), personList.getRollno());

Map<Object, List<Person>> map = personList.stream().collect(Collectors.groupingBy(compositeKey, Collectors.toList()));

List<Object> duplicateEntrys = map.entrySet().stream()`enter code here`
        .filter(settingMap ->
                settingMap.getValue().size() > 1)
        .collect(Collectors.toList());

0

내 경우에는 이전 요소를 제어해야했습니다. 그런 다음 이전 요소가 현재 요소와 다른지 여부를 제어 하는 상태 저장 술어 를 작성 했습니다.

public List<Log> fetchLogById(Long id) {
    return this.findLogById(id).stream()
        .filter(new LogPredicate())
        .collect(Collectors.toList());
}

public class LogPredicate implements Predicate<Log> {

    private Log previous;

    public boolean test(Log atual) {
        boolean isDifferent = previouws == null || verifyIfDifferentLog(current, previous);

        if (isDifferent) {
            previous = current;
        }
        return isDifferent;
    }

    private boolean verifyIfDifferentLog(Log current, Log previous) {
        return !current.getId().equals(previous.getId());
    }

}

0

이 목록에 내 솔루션 :

List<HolderEntry> result ....

List<HolderEntry> dto3s = new ArrayList<>(result.stream().collect(toMap(
            HolderEntry::getId,
            holder -> holder,  //or Function.identity() if you want
            (holder1, holder2) -> holder1 
    )).values());

내 상황에서 나는 독특한 값을 찾고 그것들을 List에 넣고 싶다.


0

가장 높은 평가를받은 답변은 Java 8을 사용하는 것이 가장 좋은 답변이지만 동시에 성능면에서는 절대적으로 최악입니다. 성능이 나쁜 나쁜 응용 프로그램을 원한다면 계속 사용하십시오. 고유 한 개인 이름 세트를 추출하기위한 간단한 요구 사항은 단지 "For-Each"및 "Set"에 의해 달성됩니다. 목록이 10보다 크면 상황이 더욱 악화됩니다.

다음과 같이 20 개의 오브젝트가 있다고 가정하십시오.

public static final List<SimpleEvent> testList = Arrays.asList(
            new SimpleEvent("Tom"), new SimpleEvent("Dick"),new SimpleEvent("Harry"),new SimpleEvent("Tom"),
            new SimpleEvent("Dick"),new SimpleEvent("Huckle"),new SimpleEvent("Berry"),new SimpleEvent("Tom"),
            new SimpleEvent("Dick"),new SimpleEvent("Moses"),new SimpleEvent("Chiku"),new SimpleEvent("Cherry"),
            new SimpleEvent("Roses"),new SimpleEvent("Moses"),new SimpleEvent("Chiku"),new SimpleEvent("gotya"),
            new SimpleEvent("Gotye"),new SimpleEvent("Nibble"),new SimpleEvent("Berry"),new SimpleEvent("Jibble"));

당신이 반대하는 곳 SimpleEvent은 다음과 같습니다.

public class SimpleEvent {

private String name;
private String type;

public SimpleEvent(String name) {
    this.name = name;
    this.type = "type_"+name;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}
}

그리고 테스트하려면 다음 과 같은 JMH 코드 가 있습니다 ( 허용 된 답변에 언급 된 동일한 distinctByKey 술어를 사용하십시오 )

@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public void aStreamBasedUniqueSet(Blackhole blackhole) throws Exception{

    Set<String> uniqueNames = testList
            .stream()
            .filter(distinctByKey(SimpleEvent::getName))
            .map(SimpleEvent::getName)
            .collect(Collectors.toSet());
    blackhole.consume(uniqueNames);
}

@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public void aForEachBasedUniqueSet(Blackhole blackhole) throws Exception{
    Set<String> uniqueNames = new HashSet<>();

    for (SimpleEvent event : testList) {
        uniqueNames.add(event.getName());
    }
    blackhole.consume(uniqueNames);
}

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
            .include(MyBenchmark.class.getSimpleName())
            .forks(1)
            .mode(Mode.Throughput)
            .warmupBatchSize(3)
            .warmupIterations(3)
            .measurementIterations(3)
            .build();

    new Runner(opt).run();
}

그러면 다음 과 같은 벤치 마크 결과가 나타납니다.

Benchmark                                  Mode  Samples        Score  Score error  Units
c.s.MyBenchmark.aForEachBasedUniqueSet    thrpt        3  2635199.952  1663320.718  ops/s
c.s.MyBenchmark.aStreamBasedUniqueSet     thrpt        3   729134.695   895825.697  ops/s

보시다시피 간단한 For-Each 는 Java 8 Stream에 비해 처리량이 3 배 향상되고 오류 점수는 적습니다.

높은 처리량, 더 나은 성능


1
감사합니다,하지만 문제는 스트림 API의 맥락에서 매우 구체적이었다
RichK

예, 동의합니다. "가장 높은 답변은 Java 8에서 절대적으로 가장 좋은 답변이지만"은 이미 언급했습니다. 문제는 여러 가지 방법으로 해결할 수 있으며 여기서는 성능 저하가 위험에 처한 Java 8 Streams로 위험하지 않고 손으로 문제를 해결할 수 있음을 강조하려고 노력하고 있습니다. :)
Abhinav Ganguly

0
Here is the example
public class PayRoll {

    private int payRollId;
    private int id;
    private String name;
    private String dept;
    private int salary;


    public PayRoll(int payRollId, int id, String name, String dept, int salary) {
        super();
        this.payRollId = payRollId;
        this.id = id;
        this.name = name;
        this.dept = dept;
        this.salary = salary;
    }
} 

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class Prac {
    public static void main(String[] args) {

        int salary=70000;
        PayRoll payRoll=new PayRoll(1311, 1, "A", "HR", salary);
        PayRoll payRoll2=new PayRoll(1411, 2    , "B", "Technical", salary);
        PayRoll payRoll3=new PayRoll(1511, 1, "C", "HR", salary);
        PayRoll payRoll4=new PayRoll(1611, 1, "D", "Technical", salary);
        PayRoll payRoll5=new PayRoll(711, 3,"E", "Technical", salary);
        PayRoll payRoll6=new PayRoll(1811, 3, "F", "Technical", salary);
        List<PayRoll>list=new ArrayList<PayRoll>();
        list.add(payRoll);
        list.add(payRoll2);
        list.add(payRoll3);
        list.add(payRoll4);
        list.add(payRoll5);
        list.add(payRoll6);


        Map<Object, Optional<PayRoll>> k = list.stream().collect(Collectors.groupingBy(p->p.getId()+"|"+p.getDept(),Collectors.maxBy(Comparator.comparingInt(PayRoll::getPayRollId))));


        k.entrySet().forEach(p->
        {
            if(p.getValue().isPresent())
            {
                System.out.println(p.getValue().get());
            }
        });



    }
}

Output:

PayRoll [payRollId=1611, id=1, name=D, dept=Technical, salary=70000]
PayRoll [payRollId=1811, id=3, name=F, dept=Technical, salary=70000]
PayRoll [payRollId=1411, id=2, name=B, dept=Technical, salary=70000]
PayRoll [payRollId=1511, id=1, name=C, dept=HR, salary=70000]

-2

다음 사람 목록을 원한다면 간단한 방법이 될 것입니다.

Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());

또한, 당신은 별개의 또는 고유 찾으려면 이름의 목록을 , 없는 사람 , 당신은뿐만 아니라이 방법에 따라 이용 할 수 있습니다.

방법 1 : 사용 distinct

persons.stream().map(x->x.getName()).distinct.collect(Collectors.toList());

방법 2 : 사용 HashSet

Set<E> set = new HashSet<>();
set.addAll(person.stream().map(x->x.getName()).collect(Collectors.toList()));

2
Persons가 아닌 이름 목록을 생성합니다 .
헐크

1
이것이 바로 내가 찾던 것입니다. 컬렉션을 서로 변환하면서 중복을 제거하기 위해 한 줄 방법이 필요했습니다. 감사.
Raj

-3

작성할 수있는 가장 간단한 코드 :

    persons.stream().map(x-> x.getName()).distinct().collect(Collectors.toList());

12
그래도 이름 별 사람이 아닌 별개의 이름 목록을 얻을 수 있습니다
RichK
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.