Java 8 Distinct by property

后端 未结 29 1834
傲寒
傲寒 2020-11-21 22:35

In Java 8 how can I filter a collection using the Stream API by checking the distinctness of a property of each object?

For example I have a list of

相关标签:
29条回答
  • 2020-11-21 23:17

    You can use groupingBy collector:

    persons.collect(Collectors.groupingBy(p -> p.getName())).values().forEach(t -> System.out.println(t.get(0).getId()));
    

    If you want to have another stream you can use this:

    persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream().map(l -> (l.get(0)));
    
    0 讨论(0)
  • 2020-11-21 23:17

    The easiest way to implement this is to jump on the sort feature as it already provides an optional Comparator which can be created using an element’s property. Then you have to filter duplicates out which can be done using a statefull Predicate which uses the fact that for a sorted stream all equal elements are adjacent:

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

    Of course, a statefull Predicate is not thread-safe, however if that’s your need you can move this logic into a Collector and let the stream take care of the thread-safety when using your Collector. This depends on what you want to do with the stream of distinct elements which you didn’t tell us in your question.

    0 讨论(0)
  • 2020-11-21 23:19
    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]
    
    0 讨论(0)
  • 2020-11-21 23:20

    Another solution, using Set. May not be the ideal solution, but it works

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

    Or if you can modify the original list, you can use removeIf method

    persons.removeIf(p -> !set.add(p.getName()));
    
    0 讨论(0)
  • 2020-11-21 23:21

    Distinct objects list can be found using:

     List distinctPersons = persons.stream()
                        .collect(Collectors.collectingAndThen(
                                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
                                ArrayList::new));
    
    0 讨论(0)
提交回复
热议问题