Java 8 使用自定义键分组

Java 8 groupingby with custom key

我有一系列格式如下的输入字符串:

typeA:code1,
typeA:code2,
typeA:code3,
typeB:code4,
typeB:code5,
typeB:code6,
typeC:code7,
...

我需要得到一个具有以下结构的 Map<String, List<String>>

typeA, [code1, code2, code3]
typeB, [code4, code5, code6]
typeC, [code7, code8, ...]

要注意的是,要生成每种类型,我需要在每个输入字符串上调用这样的函数:

public static String getType(String code)
{
  return code.split(":")[0];  // yes this is horrible code, it's just for the example, honestly
}

我非常有信心 Streams 和 Collectors 可以做到这一点,但我正在努力获得正确的咒语来实现它。

这是一种方法(假设 class 被命名为 A):

Map<String, List<String>> result = Stream.of(input)
                          .collect(groupingBy(A::getType, mapping(A::getValue, toList())));

如果您希望对输出进行排序,您可以使用 TreeMap 而不是默认的 HashMap:

.collect(groupingBy(A::getType, TreeMap::new, mapping(A::getValue, toList())));

完整示例:

public static void main(String[] args) {
  String input[] = ("typeA:code1," +
                "typeA:code2," +
                "typeA:code3," +
                "typeB:code4," +
                "typeB:code5," +
                "typeB:code6," +
                "typeC:code7").split(",");

  Map<String, List<String>> result = Stream.of(input)
                    .collect(groupingBy(A::getType, mapping(A::getValue, toList())));
  System.out.println(result);
}

public static String getType(String code) {
  return code.split(":")[0];
}
public static String getValue(String code) {
  return code.split(":")[1];
}

如果考虑到您遗漏的内容,您还需要拆分字符串的第二部分,代码就会变得简单:

Map<String, List<String>> result = Stream.of(input).map(s->s.split(":", 2))
    .collect(groupingBy(a->a[0], mapping(a->a[1], toList())));

(假设你有 import static java.util.stream.Collectors.*;

String 拆分为数组并没有错,实现甚至为您使用单个简单字符而不是复杂的正则表达式拆分的常见情况提供了“快速路径”。

虽然我太慢了,这里有一个 MCVE 展示了如何用 Collectors#groupingBy 解决这个问题。

定义 "classifier" 和 "mapper" 显然有不同的选项。这里我只是简单地使用 String#substring 来查找 ":".

之前和之后的部分
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class GroupingBySubstringsTest
{
    public static void main(String[] args)
    {
        List<String> strings = new ArrayList<String>();
        strings.add("typeA:code1");
        strings.add("typeA:code2");
        strings.add("typeA:code3");
        strings.add("typeB:code4");
        strings.add("typeB:code5");
        strings.add("typeB:code6");
        strings.add("typeC:code7");

        Map<String, List<String>> result = strings.stream().collect(
            groupingBy(s -> s.substring(0, s.indexOf(":")), 
                mapping(s -> s.substring(s.indexOf(":")+1), toList())));

        for (Entry<String, List<String>> entry : result.entrySet())
        {
            System.out.println(entry);
        }
    }
}

考虑学生 Class:

public Student(String name, Address add) {
    super();
    this.name = name;
    this.add = add;
}
private String name;
private Address add;
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public Address getAdd() {
    return add;
}
public void setAdd(Address add) {
    this.add = add;
}

}

和地址class:

class Address{

public Address(String city, String state) {
    super();
    this.city = city;
    State = state;
}
private String city;
private String State;
public String getCity() {
    return city;
}
public void setCity(String city) {
    this.city = city;
}
public String getState() {
    return State;
}
public void setState(String state) {
    State = state;
}

}

现在,如果我想根据作为地址 class 一部分的城市和州对学生进行分组:

Student s1 = new Student("Rohit", new Address("Mumbai", "MH"));
    Student s2 = new Student("Sudeep", new Address("Mumbai", "MH"));
    Student s3 = new Student("Amit", new Address("Pune", "MH"));
    Student s4 = new Student("Rahul", new Address("Blore", "KR"));
    Student s5 = new Student("Vishal", new Address("Blore", "KR"));
    
    List<Student> st =  Arrays.asList(s1,s2,s3,s4,s5);
    
    Function<Student, String> compositeKey = studRecord -> studRecord.getAdd().getCity()+":"+ studRecord.getAdd().getState();
    
    Map<String, List<Student>> groupedStudent =  st.stream()
            .collect(Collectors.groupingBy(compositeKey));