我有一个 Student 类的实例。
class Student {
String name;
String addr;
String type;
public Student(String name, String addr, String type) {
super();
this.name = name;
this.addr = addr;
this.type = type;
}
@Override
public String toString() {
return "Student [name=" + name + ", addr=" + addr + "]";
}
public String getName() {
return name;
}
public String getAddr() {
return addr;
}
}
我有一个代码来创建一个 map ,它将学生姓名存储为 key 和一些处理过的 addr
值(一个列表,因为我们有多个 addr
值)作为值。
public class FilterId {
public static String getNum(String s) {
// should do some complex stuff, just for testing
return s.split(" ")[1];
}
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>();
list.add(new Student("a", "test 1", "type 1"));
list.add(new Student("a", "test 1", "type 2"));
list.add(new Student("b", "test 1", "type 1"));
list.add(new Student("c", "test 1", "type 1"));
list.add(new Student("b", "test 1", "type 1"));
list.add(new Student("a", "test 1", "type 1"));
list.add(new Student("c", "test 3", "type 2"));
list.add(new Student("a", "test 2", "type 1"));
list.add(new Student("b", "test 2", "type 1"));
list.add(new Student("a", "test 3", "type 1"));
Map<String, List<String>> map = new HashMap<>();
// This will create a Map with Student names (distinct) and the test numbers (distinct List of tests numbers) associated with them.
for (Student student : list) {
if (map.containsKey(student.getName())) {
List<String> numList = map.get(student.getName());
String value = getNum(student.getAddr());
if (!numList.contains(value)) {
numList.add(value);
map.put(student.getName(), numList);
}
} else {
map.put(student.getName(), new ArrayList<>(Arrays.asList(getNum(student.getAddr()))));
}
}
System.out.println(map.toString());
}
}
输出将是:
{a=[1, 2, 3], b=[1, 2], c=[1, 3]}
我怎样才能以更优雅的方式在 java8 中做同样的事情,可能是使用流?
找到 this Collectors.toMap
在 Java 8 中,但无法找到一种方法来实际执行相同的操作。
我试图将元素映射为 CSV,但它没有用,因为我想不出一种轻松删除重复项的方法,而且输出不是我目前需要的。
Map<String, String> map2 = new HashMap<>();
map2 = list.stream().collect(Collectors.toMap(Student::getName, Student::getAddr, (a, b) -> a + " , " + b));
System.out.println(map2.toString());
// {a=test 1 , test 1 , test 1 , test 2 , test 3, b=test 1 , test 1 , test 2, c=test 1 , test 3}
最佳答案
对于流,您可以使用 Collectors.groupingBy
连同 Collectors.mapping
:
Map<String, Set<String>> map = list.stream()
.collect(Collectors.groupingBy(
Student::getName,
Collectors.mapping(student -> getNum(student.getAddr()),
Collectors.toSet())));
我选择创建集合映射而不是列表映射,因为您似乎不希望列表中有重复项。
如果您确实需要列表而不是集合,那么首先收集到集合然后将集合转换为列表会更有效:
Map<String, List<String>> map = list.stream()
.collect(Collectors.groupingBy(
Student::getName,
Collectors.mapping(s -> getNum(s.getAddr()),
Collectors.collectingAndThen(Collectors.toSet(), ArrayList::new))));
这使用 Collectors.collectingAndThen
,它首先收集然后转换结果。
另一种更紧凑的方式,没有流:
Map<String, Set<String>> map = new HashMap<>(); // or LinkedHashMap
list.forEach(s ->
map.computeIfAbsent(s.getName(), k -> new HashSet<>()) // or LinkedHashSet
.add(getNum(s.getAddr())));
此变体使用 Iterable.forEach
迭代列表和Map.computeIfAbsent
按学生姓名对转换后的地址进行分组。
关于java - 当我们有重复元素和自定义过滤条件时,在 Java 8 中将 List<MyObject> 转换为 Map<String, List<String>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48587730/