为了账号安全,请及时绑定邮箱和手机立即绑定

在Java 8中按多个字段名称分组

在Java 8中按多个字段名称分组

SMILET 2019-10-17 15:05:10
我找到了通过POJO中的某些字段名称对对象进行分组的代码。以下是该代码:public class Temp {    static class Person {        private String name;        private int age;        private long salary;        Person(String name, int age, long salary) {            this.name = name;            this.age = age;            this.salary = salary;        }        @Override        public String toString() {            return String.format("Person{name='%s', age=%d, salary=%d}", name, age, salary);        }    }    public static void main(String[] args) {        Stream<Person> people = Stream.of(new Person("Paul", 24, 20000),                new Person("Mark", 30, 30000),                new Person("Will", 28, 28000),                new Person("William", 28, 28000));        Map<Integer, List<Person>> peopleByAge;        peopleByAge = people                .collect(Collectors.groupingBy(p -> p.age, Collectors.mapping((Person p) -> p, toList())));        System.out.println(peopleByAge);    }}输出是(正确的):{24=[Person{name='Paul', age=24, salary=20000}], 28=[Person{name='Will', age=28, salary=28000}, Person{name='William', age=28, salary=28000}], 30=[Person{name='Mark', age=30, salary=30000}]}但是,如果我想按多个字段分组怎么办?在该POJO中groupingBy()实现equals()方法后,我显然可以在该方法中传递一些POJO,但是是否还有其他选择,例如我可以对给定POJO中的多个字段进行分组?例如,在这里,我想按姓名和年龄分组。
查看完整描述

3 回答

?
长风秋雁

TA贡献1757条经验 获得超7个赞

您在这里有一些选择。最简单的方法是链接您的收藏家:


Map<String, Map<Integer, List<Person>>> map = people

    .collect(Collectors.groupingBy(Person::getName,

        Collectors.groupingBy(Person::getAge));

然后,要使用18岁的年轻人Fred的清单,您可以使用:


map.get("Fred").get(18);

第二种选择是定义一个代表分组的类。这可以在Person内部:


class Person {

    public static class NameAge {

        public NameAge(String name, int age) {

            ...

        }


        // must implement equals and hash function

    }


    public NameAge getNameAge() {

        return new NameAge(name, age);

    }

}

然后,您可以使用:


Map<NameAge, List<Person>> map = people.collect(Collectors.groupingBy(Person::getNameAge));

并搜索


map.get(new NameAge("Fred", 18));

最后,如果您不想实现自己的组类,那么周围的许多Java框架都有一个pair专门用于此类事情的类。例如:apache commons pair如果您使用这些库之一,则可以将地图的名称和年龄配对:


Map<Pair<String, Integer>, List<Person>> map =

    people.collect(Collectors.groupingBy(p -> Pair.of(p.getName(), p.getAge())));

并使用:


map.get(Pair.of("Fred", 18));

我个人真的不喜欢这些元组库。它们似乎与良好的OO设计完全相反:它们隐藏意图而不是公开意图。


话虽如此,您可以通过定义自己的分组类来组合后两个选项,而仅通过扩展即可实现Pair-从而节省了很多定义equals等工作,并且将元组的使用隐藏为任何其他便捷的实现细节其他集合。


祝好运。


查看完整回答
反对 回复 2019-10-17
?
蓝山帝景

TA贡献1843条经验 获得超7个赞

这里看一下代码:


您可以简单地创建一个功能,让它为您完成工作,一种功能样式!


Function<Person, List<Object>> compositeKey = personRecord ->

    Arrays.<Object>asList(personRecord.getName(), personRecord.getAge());

现在,您可以将其用作地图:


Map<Object, List<Person>> map =

people.collect(Collectors.groupingBy(compositeKey, Collectors.toList()));

干杯!


查看完整回答
反对 回复 2019-10-17
?
慕斯王

TA贡献1864条经验 获得超2个赞

该groupingBy方法具有的第一个参数是Function<T,K>:


@param <T>输入元素的类型


@param <K>键的类型


如果在您的代码中将lambda替换为匿名类,则可以看到以下内容:


people.stream().collect(Collectors.groupingBy(new Function<Person, int>() {

            @Override

            public int apply(Person person) {

                return person.getAge();

            }

        }));

刚才更改输出参数<K>。例如,在这种情况下,我使用了org.apache.commons.lang3.tuple中的pair类按名称和年龄进行分组,但是您可以根据需要创建自己的类以过滤组。


people.stream().collect(Collectors.groupingBy(new Function<Person, Pair<Integer, String>>() {

                @Override

                public YourFilter apply(Person person) {

                    return Pair.of(person.getAge(), person.getName());

                }

            }));

最后,用lambda back替换后,代码如下所示:


Map<Pair<Integer,String>, List<Person>> peopleByAgeAndName = people.collect(Collectors.groupingBy(p -> Pair.of(person.getAge(), person.getName()), Collectors.mapping((Person p) -> p, toList())));


查看完整回答
反对 回复 2019-10-17
  • 3 回答
  • 0 关注
  • 4020 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信