基于条件和顺序的 Java 8 lambda 过滤

2022-09-02 09:49:51

我试图根据多个条件过滤列表,排序。

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

        public Student(int age, String className, String name) {
            Age = age;
            this.className = className;
            Name = name;
        }

        public int getAge() {
            return Age;
        }

        public void setAge(int age) {
            Age = age;
        }

        public String getClassName() {
            return className;
        }

        public void setClassName(String className) {
            this.className = className;
        }

        public String getName() {
            return Name;
        }

        public void setName(String name) {
            Name = name;
        }
    }

现在,如果我有一个清单,比如说

List<Student> students = new ArrayList<>();
        students.add(new Student(24, "A", "Smith"));
        students.add(new Student(24, "A", "John"));
        students.add(new Student(30, "A", "John"));
        students.add(new Student(20, "B", "John"));
        students.add(new Student(24, "B", "Prince"));

我如何才能获得具有独特名称的最年长学生的列表?在C#中,使用System.Linq Group会非常简单,然后比较并与select一起扁平化,我不太确定如何在Java中实现相同的目标。


答案 1

使用 toMap 收集器:

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

解释

我们正在使用以下重载:toMap

toMap​(Function<? super T,? extends K> keyMapper,
      Function<? super T,? extends U> valueMapper,
      BinaryOperator<U> mergeFunction)
  • Student::getName上面是用于提取映射键的值的函数。keyMapper
  • Function.identity()上面是用于提取映射值的值的函数,其中仅返回源它们本身(即对象)中的元素。valueMapperFunction.identity()Student
  • BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))上面是用于“在密钥碰撞的情况下决定返回哪个Student对象”的合并函数,即当两个给定的学生具有相同的名称时“,在这种情况下取最旧的。Student
  • 最后,调用会给我们带来一批学生。values()

等效的 C# 代码为:

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

解释(对于那些不熟悉.NET的人)

我们使用 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上面是用于提取值以进行分组的函数。keySelector
  • v => v上面是用于提取值的函数,即它们自己的对象。elementSelectorStudent
  • b.OrderByDescending(e => e.Age).Take(1)上面是哪个给定一个表示为最年长的学生。resultSelectorIEnumerable<Student>b
  • 最后,我们应用将结果折叠成 ..SelectMany(x => x);IEnumerable<IEnumerable<Student>>IEnumerable<Student>

答案 2

或者没有流:

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

推荐