programing

Java 8 속성별 구별

prostudy 2022. 7. 8. 22:07
반응형

Java 8 속성별 구별

8을 합니까?StreamAPI api api 、 API 、 api api api api api api api api api api?

를 들어, 제가 가 있습니다.Person같은 .

persons.stream().distinct();

합니다.Person하기 때문에, 예를 들면 「오브젝트」라고 하는 합니다.

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

도 ★★★★★★★★★★★★★★★.distinct()메서드에 이러한 오버로드가 없습니다. Person업을간 간결 ?결? ???? ???

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()에 대한 제 답변과 동일합니다.)

다른 방법으로는 이름을 키로 지도에 인물을 배치하는 방법이 있습니다.

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

이름이 중복될 경우 보관되는 사용자가 첫 번째로 변환됩니다.

사용자 오브젝트를 다른 클래스로 줄바꿈하여 사용자 이름만 비교할 수 있습니다.그 후 랩된 오브젝트를 풀어서 사람 스트림을 다시 가져옵니다.스트림 조작은 다음과 같습니다.

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

다른 해결법은 '보다 낫다', '보다'를 요.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()));

커스텀 컴퍼레이터와 함께 TreeSet를 사용하는 것이 보다 심플한 방법입니다.

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

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

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

또는

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

하시면 됩니다.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)));

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

Hashing Strategy는 동등 및 해시 코드의 커스텀 구현을 정의할 수 있는 단순한 전략 인터페이스입니다.

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

주의: 저는 Eclipse Collections의 커밋입니다.

Saed Zarinfam이 사용한 접근방식은 유사하지만 Java 8 스타일이 더 많다.

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

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

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

가능하다면 Vavr 사용을 권장합니다.이 라이브러리를 사용하여 다음 작업을 수행할 수 있습니다.

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

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

범용 버전을 만들었습니다.

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

개별 개체 목록은 다음을 사용하여 찾을 수 있습니다.

 List distinctPersons = persons.stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
                            ArrayList::new));
Set<YourPropertyType> set = new HashSet<>();
list
        .stream()
        .filter(it -> set.add(it.getYourProperty()))
        .forEach(it -> ...);

이를 지원하는 다른 라이브러리는 jOO와 그 방식입니다.

Seq.seq(persons).distinct(Person::getName).toList();

하지만, 위장 아래에서는, 사실상 인정된 답변과 같은 일을 합니다.

나의 은, 을 가지는 모든 오브젝트를후, 1의해, 을 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());

가장 높은 투표율이 높은 답변은 Java 8에 대한 최고의 답변이지만 동시에 성능 면에서는 절대적으로 최악입니다.성능이 나쁜 애플리케이션을 정말로 필요로 하는 경우는, 그것을 사용해 주세요.하나의 개인명 세트를 추출하는 간단한 요건은 "각자" 및 "세트"에 의해 달성되어야 한다.리스트가 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 코드가 있습니다(접수된 답변에 기재된 것과 동일한 distinentByKey Predicate를 사용하고 있습니다).

@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에 비해 throughput이 3배 향상되고 오류 점수가 낮아집니다.

더 높은스루풋, 퍼포먼스 향상

은, 의 「」가 되어 있기 에, 「」로 입니다.Comparator요소의 속성을 사용하여 생성할 수 있습니다.할 수 .Predicate이는 정렬된 스트림에 대해 모든 동일한 요소가 인접해 있다는 사실을 사용합니다.

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 */;

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」Predicate세이프가 , 이를, 이 논리를, 이 논리를, 이 논리를, 이 논리를, 이 논리로, 이 논리를, 이 논리를, 이 논리로 수.Collector할 때 .Collector이는 질문에서 설명하지 않은 개별 요소의 흐름을 어떻게 처리하느냐에 따라 달라집니다.

나는 스튜어트 마크스의 답변을 개선하고 싶다.키가 null이면 어떻게 하죠?NullPointerException여기에서는 null 키를 무시하고 체크를 1개 더 추가합니다.keyExtractor.apply(t)!=null.

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> keyExtractor.apply(t)!=null && seen.add(keyExtractor.apply(t));

}

이것은 매우 효과적입니다.

  1. 고유 키로 데이터를 그룹화하여 맵을 형성합니다.
  2. 맵의 모든 값에서 첫 번째 개체를 반환한다(같은 이름을 가진 여러 사람이 있을 수 있음).
persons.stream()
    .collect(groupingBy(Person::getName))
    .values()
    .stream()
    .flatMap(values -> values.stream().limit(1))
    .collect(toList());
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]

파티에는 늦었지만 나는 가끔 이 원라이너를 동등한 것으로 사용한다.

((Function<Value, Key>) Value::getKey).andThen(new HashSet<>()::add)::apply

표현은Predicate<Value>맵은 인라인이기 때문에 필터로서 기능합니다.물론 읽기 쉽지는 않지만 방법을 피하는 것이 도움이 될 수 있습니다.

@josketres의 답변을 바탕으로 범용 유틸리티 메서드를 만들었습니다.

Collector를 생성하여 Java 8을 보다 쉽게 만들 수 있습니다.

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

아마 누군가에게 유용할 것이다.을 사용하다A로부터 같은 A.b " " " A.id (비밀(이행)A 것을 A.id(목록에 기재되어 있습니다.Tagir Valeev스트림 파티션 응답은 커스텀을 사용하도록 영감을 주었습니다.Collector 결과, 반환하다Map<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)); }

두 개의 키를 기준으로 목록에서 다른 요소를 얻어야 하는 상황이 있었습니다.두 개의 키를 기준으로 구분하거나 복합 키를 사용하려면 다음과 같이 하십시오.

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

를 처리하는 상위 답변의 변형입니다.null:

    public static <T, K> Predicate<T> distinctBy(final Function<? super T, K> getKey) {
        val seen = ConcurrentHashMap.<Optional<K>>newKeySet();
        return obj -> seen.add(Optional.ofNullable(getKey.apply(obj)));
    }

내 테스트:

        assertEquals(
                asList("a", "bb"),
                Stream.of("a", "b", "bb", "aa").filter(distinctBy(String::length)).collect(toList()));

        assertEquals(
                asList(5, null, 2, 3),
                Stream.of(5, null, 2, null, 3, 3, 2).filter(distinctBy(x -> x)).collect(toList()));

        val maps = asList(
                hashMapWith(0, 2),
                hashMapWith(1, 2),
                hashMapWith(2, null),
                hashMapWith(3, 1),
                hashMapWith(4, null),
                hashMapWith(5, 2));

        assertEquals(
                asList(0, 2, 3),
                maps.stream()
                        .filter(distinctBy(m -> m.get("val")))
                        .map(m -> m.get("i"))
                        .collect(toList()));

많은 접근법이 있습니다.이 접근법도 도움이 됩니다.

    List<Employee> employees = new ArrayList<>();

    employees.add(new Employee(11, "Ravi"));
    employees.add(new Employee(12, "Stalin"));
    employees.add(new Employee(23, "Anbu"));
    employees.add(new Employee(24, "Yuvaraj"));
    employees.add(new Employee(35, "Sena"));
    employees.add(new Employee(36, "Antony"));
    employees.add(new Employee(47, "Sena"));
    employees.add(new Employee(48, "Ravi"));

    List<Employee> empList = new ArrayList<>(employees.stream().collect(
                    Collectors.toMap(Employee::getName, obj -> obj,
                    (existingValue, newValue) -> existingValue))
                   .values());

    empList.forEach(System.out::println);


    //  Collectors.toMap(
    //  Employee::getName, - key (the value by which you want to eliminate duplicate)
    //  obj -> obj,  - value (entire employee object)
    //  (existingValue, newValue) -> existingValue) - to avoid illegalstateexception: duplicate key

출력 - toString()이 오버로드되었습니다.

Employee{id=35, name='Sena'}
Employee{id=12, name='Stalin'}
Employee{id=11, name='Ravi'}
Employee{id=24, name='Yuvaraj'}
Employee{id=36, name='Antony'}
Employee{id=23, name='Anbu'}

내 경우, 나는 이전의 요소를 제어할 필요가 있었다.그런 다음 이전 요소가 현재 요소와 다른지 제어하는 상태 저장 술어를 만들었습니다. 이 경우, 저는 이 술어를 유지합니다.

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

}

이 리스트의 솔루션:

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

제 상황에서는 다른 가치를 찾아서 목록에 넣고 싶습니다.

언급URL : https://stackoverflow.com/questions/23699371/java-8-distinct-by-property

반응형