Java集合 - 按多个属性分组

时间:2017-09-04 13:54:39

标签: java collections group-by

我有一个Java类:

class Person {
  String firstName;
  String lastName;
  int income;

public Person(String firstName, String lastName, int income)
{
  this.firstName = firstName;
  this.lastName = lastName;
  this.income = income;
}

我有一个Collection<Person>,有4个人物对象:

  Collection<Person> persons = new ArrayList<>();
  persons.add(new Person("John", "Smith", 5));
  persons.add(new Person("Mary", "Miller", 2));
  persons.add(new Person("John", "Smith", 4));
  persons.add(new Person("John", "Wilson", 4));

我想创建一个新的Collection实例,但是来自具有相同&#34; firstName&#34;的元素。和&#34; lastName&#34;,制作1个元素,结果&#34;收入&#34;将是&#34;收入的总和&#34;每个元素。因此,对于这种特殊情况,最终的集合将有3个元素,&#34; John Smith&#34;将有&#34;收入&#34; = 9.

在SQL中,等效查询是:

SELECT FIRSTNAME, LASTNAME, SUM(INCOME) FROM PERSON GROUP BY FIRSTNAME, LASTNAME

我发现只有包含Map作为结果的答案,以及&#34; key&#34;包含用于分组的列。我想从初始(ArrayList<Person>)直接获得类似的集合,而不是Map,因为如果在我的集合中我有数百万个元素,它将降低代码的性能。我知道如果我在SQL方面工作会更容易,但在这种情况下我必须在Java方面工作。

4 个答案:

答案 0 :(得分:2)

我不知道这是否是最美丽的解决方案,但您可以尝试options: () => ({ variables: { count: 10, current: 1 }, fetchPolicy: 'network-only', notifyOnNetworkStatusChange: true }) groupByfirstName之间的分隔符,让我们来看看说lastName。在将数据收集到包含.的{​​{1}}后,您可以从中创建Map<String, Integer>的新列表。

firstName.lastName

答案 1 :(得分:1)

我找到了以下答案:

List<Person> collect = persons.stream()
            .collect(Collectors.groupingBy(person -> person.getFirstName() + "." + person.getLastName(),
                    Collectors.summingInt(Person::getIncome)))
            .entrySet().stream().map(entry -> new Person(entry.getKey().split(".")[0],
                                                                      entry.getKey().split(".")[1],
                                                                      entry.getValue()))
            .collect(Collectors.toList());

不要那样做!它使用了很多内存。在您需要分组的字段上使用Wrapper(PersonComparator)。

public class Main {
    public static void main(String[] args) {
        Collection<Person> persons = new ArrayList<>();
        persons.add(new Person("John", "Smith", 5));
        persons.add(new Person("Mary", "Miller", 2));
        persons.add(new Person("John", "Smith", 4));
        persons.add(new Person("John", "Wilson", 4));

        Map<Person, Integer> groupedByIncomes = persons.stream()
                .collect(
                        Collectors.groupingBy(
                                Person::getPersonComparator,
                                Collectors.summingInt(Person::getIncome)
                        )
                )
                .entrySet()
                .stream()
                .collect(Collectors.toMap(
                        e -> e.getKey().person,
                        Map.Entry::getValue
                ));

        System.out.println(groupedByIncomes);
    }

    static class Person {
        String firstName;
        String lastName;
        int income;

        public Person(String firstName, String lastName, int income) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.income = income;
        }

        public String getFirstName() {
            return firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public int getIncome() {
            return income;
        }

        PersonComparator getPersonComparator() {
            return new PersonComparator(this);
        }

        static class PersonComparator {
            Person person;

            PersonComparator(Person person) {
                this.person = person;
            }

            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                PersonComparator that = (PersonComparator) o;
                if (!person.getFirstName().equals(that.person.getFirstName())) return false;
                return person.getLastName().equals(that.person.getLastName());
            }

            @Override
            public int hashCode() {
                int result = person.getFirstName().hashCode();
                result = 31 * result + person.getLastName().hashCode();
                return result;
            }
        }
    }
}

如果你需要框架解决方案f.e.当你需要对你拥有的数据类型(SQL,Mongo或Collections)进行抽象时,我建议你使用QueryDSL:http://www.querydsl.com/

答案 2 :(得分:0)

您可以使用Java 8流'Collector's groupingBy:

    Map<String, Integer> sum = items.stream().collect(
            Collectors.groupingBy(p -> p.getFirstName()+p.getSecondName(), Collectors.summingInt(Person::getIncome)));

答案 3 :(得分:0)

我认为 JoSQL 是你的方式,它允许你在java对象上运行SQL查询:

  

JoSQL SQL for Java Objects )使开发人员能够将SQL语句应用于Java对象集合。 JoSQL提供了搜索,排序和分组任何Java对象的能力,当你想对Java对象集合执行类似SQL的查询时,应该应用它。

这就是如何在你的情况下使用它:

Query q=new Query();
q.parse("SELECT firstname, lastname, SUM(income) FROM package.Person GROUP BY firstname, lastname");
List<?> results=q.execute(names).getResults();

您也可以按照 JoSQL tutorial 进一步阅读。

相关问题