java - 当我们有重复元素和自定义过滤条件时,在 Java 8 中将 List<MyObject> 转换为 Map<String, List<String>>

标签 java collections java-stream

我有一个 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/

相关文章:

Java 设置异常

java - 这里的 Optional<Class> 有什么问题?

java - 如何验证用户输入的是 double 型还是整数?

sorting - Smalltalk 集合

屏幕变黑后,Java Android 套接字立即被杀死

java - 如何参数化扩展集合

java - 一次性收集 Java Stream 的平均值

java - 将 java Stream 转换为 Set

java - Struts2 jasperreports-插件 : unable to generate report using jdbc connection

java - Args 保证非空?