Java 8 lambda filtering based on condition as well as order

后端 未结 4 702
清酒与你
清酒与你 2021-01-01 13:36

I was trying to filter a list based on multiple conditions, sorting.

class Student{
        private int Age;
        private String className;
        privat         


        
相关标签:
4条回答
  • 2021-01-01 14:04

    If you need a grouping only sorted, it is quite simple:

    Map<String, List<Student>> collect = students.stream() // stream capabilities
            .sorted(Comparator.comparingInt(Student::getAge).reversed()) // sort by age, descending
            .collect(Collectors.groupingBy(Student::getName)); // group by name.
    

    Output in collect:

    • Prince=[Student [Age=24, className=B, Name=Prince]],
    • Smith=[Student [Age=24, className=A, Name=Smith]],
    • John=[Student [Age=30, className=A, Name=John], Student [Age=24, className=A, Name=John], Student [Age=20, className=B, Name=John]]
    0 讨论(0)
  • 2021-01-01 14:06

    Use the toMap collector:

    Collection<Student> values = students.stream()
                    .collect(toMap(Student::getName,
                            Function.identity(),
                            BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))))
                    .values();
    

    Explanation

    We're using this overload of toMap:

    toMap​(Function<? super T,? extends K> keyMapper,
          Function<? super T,? extends U> valueMapper,
          BinaryOperator<U> mergeFunction)
    
    • Student::getName above is the keyMapper function used to extract the values for the map keys.
    • Function.identity() above is the valueMapper function used to extract the values for the map values where Function.identity() simply returns the elements in the source them selves i.e. the Student objects.
    • BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge)) above is the merge function used to "decide which Student object to return in the case of a key collission i.e. when two given students have the same name" in this case taking the oldest Student .
    • Finally, invoking values() returns us a collection of students.

    The equivalent C# code being:

    var values = students.GroupBy(s => s.Name, v => v,
                              (a, b) => b.OrderByDescending(e => e.Age).Take(1))
                          .SelectMany(x => x);
    

    Explanation (for those unfamiliar with .NET)

    We're using this extension method of GroupBy:

    System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource,TKey,TElement,TResult> 
           (this System.Collections.Generic.IEnumerable<TSource> source, 
             Func<TSource,TKey> keySelector, 
             Func<TSource,TElement> elementSelector, 
         Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult> resultSelector);
    
    • s => s.Name above is the keySelector function used to extract the value to group by.
    • v => v above is the elementSelector function used to extract the values i.e. the Student objects them selves.
    • b.OrderByDescending(e => e.Age).Take(1) above is the resultSelector which given an IEnumerable<Student> represented as b takes the oldest student.
    • Finally, we apply .SelectMany(x => x); to collapse the resulting IEnumerable<IEnumerable<Student>> into a IEnumerable<Student>.
    0 讨论(0)
  • 2021-01-01 14:12

    Or without streams:

    Map<String, Student> map = new HashMap<>();
    students.forEach(x -> map.merge(x.getName(), x, (oldV, newV) -> oldV.getAge() > newV.getAge() ? oldV : newV));
    Collection<Student> max = map.values();
    
    0 讨论(0)
  • 2021-01-01 14:18

    Just to mix and merge the other solutions, you could alternatively do :

    Map<String, Student> nameToStudentMap = new HashMap<>();
    Set<Student> finalListOfStudents = students.stream()
            .map(x -> nameToStudentMap.merge(x.getName(), x, (a, b) -> a.getAge() > b.getAge() ? a : b))
            .collect(Collectors.toSet());
    
    0 讨论(0)
提交回复
热议问题