Java 8 속성별 구별
8을 합니까?Stream
API 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));
할 수 persons
Eclipse 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));
}
이것은 매우 효과적입니다.
- 고유 키로 데이터를 그룹화하여 맵을 형성합니다.
- 맵의 모든 값에서 첫 번째 개체를 반환한다(같은 이름을 가진 여러 사람이 있을 수 있음).
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
'programing' 카테고리의 다른 글
vue 컴포넌트에 html 전달 (0) | 2022.07.08 |
---|---|
Vue 라우터가 vue CLI 빌드에서 작동하지 않음 (0) | 2022.07.08 |
Vue2.js - 오브젝트 내의 메서드를 호출하여 콜백합니다(이 문제). (0) | 2022.07.08 |
Java 글로벌 변수 (0) | 2022.07.08 |
Java에서 스레드를 종료하려면 어떻게 해야 합니까? (0) | 2022.07.08 |