简体   繁体   中英

Java 8 stream groupingBy String value

I have a JSON file containing data in the form:

{
    "type":"type1",
    "value":"value1",
    "param": "param1"
}
{
    "type":"type2",
    "value":"value2",
    "param": "param2"
}

I also have an object like this:

public class TestObject {
    private final String value;
    private final String param;

    public TestObject(String value, String param) {
        this.value = value;
        this.param = param;
    }
}

What I want is to create a Map<String, List<TestObject>> that contains a list of TestObject s for each type.

This is what I coded:

Map<String, List<TestObject>> result = jsonFileStream
                .map(this::buildTestObject)
                .collect(Collectors.groupingBy(line -> JsonPath.read(line, "$.type")));

Where the method buildTestObject is:

private TestObject buildTestObject(String line) {
    return new TestObject(
               JsonPath.read(line, "$.value"),
               JsonPath.read(line, "$.param"));
}

This does not work because the map() function returns a TestObject , so that the collect function does not work on the JSON String line anymore.

In real life, I cannot add the "type" variable to the TestObject file, as it is a file from an external library.

How can I group my TestObject s by the type in the JSON file?

You can move the mapping operation to a down stream collector of groupingBy:

Map<String, List<TestObject>> result = jsonFileStream
    .collect(Collectors.groupingBy(line -> JsonPath.read(line, "$.type"),
        Collectors.mapping(this::buildTestObject, Collectors.toList())));

This will preserve the string so you can extract the type as a classifier, and applies the mapping to the elements of the resulting groups.

You can also use the toMap collector to accomplish the task at hand.

Map<String, List<TestObject>> resultSet = jsonFileStream
           .collect(Collectors.toMap(line -> JsonPath.read(line, "$.type"),
                  line -> new ArrayList<>(Collections.singletonList(buildTestObject(line))),
                  (left, right) -> {
                      left.addAll(right);
                      return left;
                 }
           ));

In addition to the Stream solution, it's worth pointing out that Java 8 also significantly improved the Map interface, making this kind of thing much less painful to achieve with a for loop than had previously been the case. I am not familiar with the library you are using, but something like this will work (you can always convert a Stream to an Iterable ).

Map<String, List<TestObject>> map = new HashMap<>();
for (String line : lines) {
    map.computeIfAbsent(JsonPath.read(line, "$.type"), k -> new ArrayList<>())
       .add(buildTestObject(line)); 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM