按元素的多个属性对 Java 列表进行分组

问题描述 投票:0回答:4

我有一个对象列表,其中包含基于多个属性的重复项。我想以这种方式转换这个列表。我认为这就像多个属性的分组

示例:

id name age job
1  Jane 25  job1
1  Jane 25  job2
1  Jane 25  job3
2  Adam 30  job4
2  Adam 30  job5

我希望这个列表变成这样:

1 Jane 25  (job1, job2, job3)
2 Adam 30  (job4, job5)

工作列表应该是

List<String>

java list duplicates stream
4个回答
2
投票

您期望的是按 ID 对人员进行分组。使用 Java Stream API 很方便,比如this:

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

// Create the class that represent the data type of each person
record Person(long id, String name, int age, String job) {
    @Override
    public String toString() {
        return job;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Person> peopleList = List.of(
                new Person(1, "Jane", 25, "job1"),
                new Person(1, "Jane", 25, "job2"),
                new Person(1, "Jane", 25, "job3"),
                new Person(2, "Adam", 30, "job4"),
                new Person(2, "Adam", 30, "job5")
        );
        // Please use the API to solve the problem
        Map<Long, List<Person>> peopleMap = peopleList.stream()
                .collect(Collectors.groupingBy(Person::id));
        peopleMap.forEach((id, people) -> System.out.printf("id = %d, job = %s\n", id, people.toString()));
    }
}

之前的版本将

id
视为唯一键,我们相应地按
id
对人员进行分组。然而,由于我们希望包含 3 个属性,因此我们可以重新定义 Person 类。这是修订版

record Keys(long id, String name, int age) {
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Keys keys)) return false;
        return id() == keys.id();
    }

    @Override
    public int hashCode() {
        return Objects.hash(id());
    }
}

class Person2 {
    private final Keys keys;
    private final String job;

    public Person2(long id, String name, int age, String job) {
        this.keys = new Keys(id, name, age);
        this.job = job;
    }

    public Keys getKeys() {
        return keys;
    }

    public String getJob() {
        return job;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person2 person2)) return false;
        return Objects.equals(keys, person2.keys);
    }

    @Override
    public int hashCode() {
        return Objects.hash(keys, job);
    }
}

class Answer {
    public static void main(String[] args) {
        List<Person2> peopleList = List.of(
                new Person2(1, "Jane", 25, "job1"),
                new Person2(1, "Jane", 25, "job2"),
                new Person2(1, "Jane", 25, "job3"),
                new Person2(2, "Adam", 30, "job4"),
                new Person2(2, "Adam", 30, "job5")
        );
        Map<Keys, List<Person2>> peopleMap = peopleList.stream()
                .collect(Collectors.groupingBy(Person2::getKeys));
        peopleMap.forEach((keys, person2List) -> {
            System.out.printf("%d %s %d ", keys.id(), keys.name(), keys.age());
            System.out.println(person2List.stream().map(Person2::getJob).toList());
        });
    }
}

输出如下: 1 简 25 [工作 1、工作 2、工作 3] 2 亚当 30 [工作 4,工作 5]。


0
投票

您可以利用 HashMap 类,并生成 keyvalue 对。

首先,考虑以下类,作为使用 idnameagejob 值封装 object 的方法。

class ValueA {
    int id, age;
    String name, job;

    public ValueA(int id, String name, int age, String job) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.job = job;
    }
}

此外,由于 key 应该同时是 nameage,我们将使用另一个类。
注意,这里的 toString 用于调试,不是必需的。

class ValueB {
    String name;
    int age;

    public ValueB(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + " " + age;
    }
}

您需要迭代 ValueAList,以填充 Map
此外,为了识别key,在迭代过程中,我们需要在Map上有一个内部for循环

public static void main(String[] args) {
    List<ValueA> list = new ArrayList<>();
    list.add(new ValueA(1, "Jane", 25, "job1"));
    list.add(new ValueA(1, "Jane", 25, "job2"));
    list.add(new ValueA(1, "Jane", 25, "job3"));
    list.add(new ValueA(2, "Adam", 30, "job4"));
    list.add(new ValueA(2, "Adam", 30, "job5"));

    Map<ValueB, List<String>> map = new HashMap<>();
    ValueB key;
    List<String> value;
    for (ValueA valueA : list) {
        key = new ValueB(valueA.name, valueA.age);
        value = new ArrayList<>();
        for (Map.Entry<ValueB, List<String>> entry : map.entrySet()) {
            if (entry.getKey().name.equals(valueA.name) && entry.getKey().age == valueA.age) {
                key = entry.getKey();
                value = entry.getValue();
                break;
            }
        }
        value.add(valueA.job);
        map.put(key, value);
    }

    System.out.println(map);
}

输出

{Jane 25=[job1, job2, job3], Adam 30=[job4, job5]}

0
投票

查看样本数据集,看起来

name
age
是基于
id
的。换句话说,给定两个具有相同
id
的值,
name
age
将相同。假设这是正确的,以下内容将转换输入项目的列表,并将其转换为包含
id
name
age
的项目列表以及所有作业的列表。

record PersonJob(int id, String name, int age, String job) {}
record PersonJobs(int id, String name, int age, List<String> jobs) {
    PersonJobs(List<PersonJob> l) {
        this(l.getFirst().id(), l.getFirst().name(), l.getFirst().age(),
                l.stream().map(PersonJob::job).toList());
    }
}

List<PersonJob> list = List.of(
        new PersonJob(1, "Jane", 25, "job1"),
        new PersonJob(1, "Jane", 25, "job2"),
        new PersonJob(1, "Jane", 25, "job3"),
        new PersonJob(2, "Adam", 30, "job4"),
        new PersonJob(2, "Adam", 30, "job5")
);

Map<Integer, PersonJobs> map = list.stream()
        .collect(Collectors.groupingBy(
                PersonJob::id,
                Collectors.collectingAndThen(Collectors.toList(), PersonJobs::new)));
List<PersonJobs> resultList = List.copyOf(map.values());

这将按

id
对项目进行分组,然后对于每个
id
和具有该 ID 的值列表,创建一个新对象,其中包含
id
name
age
以及与该 ID 对应的作业。


0
投票

以下内容将转换您输入的项目列表,并将其转换为映射,其中每个键是 ID/姓名/年龄组合,值是该人的工作列表。

record PersonJob(int id, String name, int age, String job) {}
record Person(int id, String name, int age) {
    Person(PersonJob s) {
        this(s.id(), s.name(), s.age());
    }
}

List<PersonJob> list = List.of(
        new PersonJob(1, "Jane", 25, "job1"),
        new PersonJob(1, "Jane", 25, "job2"),
        new PersonJob(1, "Jane", 25, "job3"),
        new PersonJob(2, "Adam", 30, "job4"),
        new PersonJob(2, "Adam", 30, "job5")
);

Map<Person, List<String>> map = list.stream()
        .collect(Collectors.groupingBy(
                Person::new,
                Collectors.mapping(PersonJob::job, Collectors.toList())));

这会根据独特的

id
/
name
/
age
组合对所有项目进行分组,将每个项目映射到与该组合相对应的所有作业的
List

© www.soinside.com 2019 - 2024. All rights reserved.