我有一个对象列表,其中包含基于多个属性的重复项。我想以这种方式转换这个列表。我认为这就像多个属性的分组
示例:
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>
。
您期望的是按 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]。
您可以利用 HashMap 类,并生成 key 和 value 对。
首先,考虑以下类,作为使用 id、name、age 和 job 值封装 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 应该同时是 name 和 age,我们将使用另一个类。
注意,这里的 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;
}
}
您需要迭代 ValueA 的 List,以填充 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]}
查看样本数据集,看起来
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 对应的作业。
以下内容将转换您输入的项目列表,并将其转换为映射,其中每个键是 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
。