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

时间:2018-12-05 14:55:06

标签: java lambda java-8 java-stream

我试图根据多种条件对列表进行排序。

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中实现相同的目标。

4 个答案:

答案 0 :(得分:18)

使用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::getNamekeyMapper函数,用于提取地图键的值。
  • 上面的
  • Function.identity()是一种valueMapper函数,用于提取映射值的值,其中Function.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.NamekeySelector函数,用于提取要分组的值。
  • 上面的
  • v => velementSelector函数,用于提取值,即Student对象本身。
  • 上面的
  • b.OrderByDescending(e => e.Age).Take(1)resultSelector,其中给定为IEnumerable<Student>的{​​{1}}接受了年龄最大的学生。
  • 最后,我们应用b将生成的.SelectMany(x => x);折叠成IEnumerable<IEnumerable<Student>>

答案 1 :(得分:7)

或者没有流:

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

答案 2 :(得分:2)

如果您只需要对分组进行排序,则非常简单:

array['']

收集中的输出:

  • Prince = [学生[Age = 24,className = B,Name = Prince]],
  • Smith = [学生[Age = 24,className = A,Name = Smith]],
  • John = [学生[Age = 30,className = A,Name = John],学生[Age = 24,className = A,Name = John],学生[Age = 20,className = B,Name = John] ]

答案 3 :(得分:1)

只需混合和合并其他解决方案,您也可以执行以下操作:

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