Reputation: 1033
I am looking for a way to Stream
a List
into a Map
where I can specify the key.
For example say I have two lists:
List<String> one = Arrays.asList("1","2","3");
List<String> two = Arrays.asList("100","200","300");
I am looking for a way to Stream
them into a Map<String, List<String>>
{
"below 100" : ["1","2","3"],
"above 100" : ["100","200","300"]
}
Any help is greatly appreciated. So far, this is what I have come up with:
Stream.of(
one.stream(),
.collect(Collectors.toMap("below 100", ??)
,
two.stream()
.collect(Collectors.toMap("above 100", ??)
)
.reduce(Stream::concat)
Upvotes: 0
Views: 11247
Reputation: 106
I found 2 similar ways. In both ways, we seperately transform each List into Map> and then join both Maps. First option is shorter and uses inline Collector, while second one creates a new Collector class.
For both options, we first declare the lists:
List<String> one = Arrays.asList("1", "2", "3");
List<String> two = Arrays.asList("100", "200", "300");
Shorter option with inline Collector:
private BinaryOperator<List<String>> addToList() {
return (list, str) -> {
((ArrayList<String>) list).addAll(str);
return list;
};
}
Map<String, List<String>> map = Stream.of(
// first list
one.stream()
.collect(Collectors.toMap(
l -> "below 100",
// we need List as map value
l -> Stream.of(l).collect(Collectors.toList()),
// merge function
addToList(),
// provide HashMap for result
HashMap::new
// we can't stream collected Map directly, only through EntrySet
)).entrySet(),
// second list
two.stream()
.collect(Collectors.toMap(
l -> "above 100",
l -> Stream.of(l).collect(Collectors.toList()),
addToList(),
HashMap::new
)).entrySet()
)
// extract Entry<String, List<String>>
.flatMap(entrySet -> entrySet.stream())
// convert Entry<String, List<String>> to Map<String, List<String>
.collect(Collectors.toMap(
entry -> entry.getKey(),
entry -> entry.getValue()));
Option using Custom Collector:
Original stream code is shorter and just calls ListToMapCollector instead of implementing inline Collector.
Map<String, List<String>> map = Stream
.of(
one.stream()
// use custom ListToMapCollector
.collect(new ListToMapCollector("below 100"))
// we can't stream collected Map directly, only through EntrySet
.entrySet(),
two.stream()
.collect(new ListToMapCollector("above 100"))
.entrySet())
// extract Entry<String, List<String>>
.flatMap(entrySet -> entrySet.stream())
// convert Entry<String, List<String>> to Map<String, List<String>
.collect(Collectors.toMap(
entry -> entry.getKey(),
entry -> entry.getValue()));
And the ListToMapCollector, I used this tutorial when creating it:
public class ListToMapCollector implements Collector<String, Map<String,
List<String>>, Map<String, List<String>>>
{
private String mapKey;
public TestCollector(String string)
{
this.mapKey = string;
}
@Override
public Supplier<Map<String, List<String>>> supplier() {
// provide HashMap for result
return HashMap::new;
}
@Override
public BiConsumer<Map<String, List<String>>, String> accumulator() {
return (map, stringValue) -> {
if (!map.containsKey(mapKey))
{
map.put(mapKey, new ArrayList<>());
}
map.get(mapKey).add(stringValue);
};
}
@Override
public BinaryOperator<Map<String, List<String>>> combiner() {
// Needed for parrallel stream, excluded for brevity.
return null;
}
@Override
public Function<Map<String, List<String>>, Map<String, List<String>>> finisher() {
return Function.identity();
}
@Override
public Set<java.util.stream.Collector.Characteristics> characteristics() {
return Collections.singleton(Characteristics.IDENTITY_FINISH);
}
}
Upvotes: 3
Reputation: 1150
EDIT:
Stream.of(
one.stream(),
two.stream()
)
.reduce(Stream::concat)
.collect(Collectors.toMap(s -> {
// if s > 100 then stuff otherwise other stuff
}, Function.identity());
Not sure if I have understand your problem well, but you can try something like this:
Map map = new HashMap<String, List<String>>();
map.put("Below 100", new ArrayList<String>());
map.put("Above 100", new ArrayList<String>());
List one = Arrays.asList("1","2","3");
List two = Arrays.asList("100","200","300");
one.stream().forEach(s -> map.get("Below 100").add(s));
two.stream().forEach(s -> map.get("Above 100").add(s));
Or alternatively, you can put some more advance code into your stream function:
one.stream().forEach(s -> {
// if value > 100 put in here,
// Otherwise do stuff...
});
Upvotes: 0