简体   繁体   中英

Create map of segregated values from two lists in stream

I'm trying to create map of two lists. First List is used for keys and second to segregate and assign values, wherein situation, where there are no values matching bin assigned, will be empty List. Values should be assigned like to key 5 values <50, 60), so having

sampleBins = Arrays.asList(0, 10, 20, 30, 40, 50, 60);

samples = Arrays.asList(5, 6, 55, 52);

I'd like to have Map that will have keys: 0, 1, 2, 3, 4, 5, 6. Empty Lists for key 1, 2, 3, 4, 6. List out of 5, 6 assigned to key 0 and 55, 52 assigned to 5.

Right now I have it like this, it's working but I'm searching for something better, for example, solution that will not require sampleBins and will fit in one pipeline

private Map<Integer, List<Integer>> segregateSamples(List<Double> sampleBins, List<Double> samples) {

    Map<Integer, List<Integer>> segregatedSamples = sampleBins.stream()
        .collect(Collectors.toMap(bin -> bin.intValue() / 10, bin -> new ArrayList<>()));

    samples.stream()
        .map(Double::intValue)
        .collect(Collectors.groupingBy(this::binNumber))
        .forEach(segregatedSamples::put);

    return segregatedSamples;
}

private Integer binNumber(int value) {
    return Math.floorDiv(value, 10);
}

Here's my first solution, which essentially combines yours into a single statement. Note that this is expensive to perform with large lists, so I will look for something more efficient in the meantime:

sampleBins.stream()
          .collect(Collectors.toMap(bin -> bin / 10, bin ->
              samples.stream()
                     .filter(i -> i - i % 10 == bin)
                     .collect(Collectors.toList())
          ));

This outputs the following:

{0=[5, 6], 1=[], 2=[], 3=[], 4=[], 5=[55, 52], 6=[]}

Here is another solution which is pretty simple:

samples.stream().collect(Collectors.groupingBy(i -> i - i % 10));

However, it will output the following instead of setting the other bins to empty List s, which may or may not be a problem in your case since you can simply check if the bin exists as a key in the Map :

{0=[5, 6], 50=[55, 52]}

I'll edit my post if I find anything else interesting, so keep an eye out!

It seems to me that the method to classify your samples is fixed. The only difference the bins are making is to specify what you are interested in. Therefore, in the first place, there is no too much need to pay attention to your bins.

Map<Integer, List<Integer>> segregateSamples(List<Integer> sampleBins, List<Integer> samples) {

    Map<Integer, List<Integer>> groupedSamples = samples.stream().collect(Collectors.groupingBy(x -> 10 * x / 10));

    return sampleBins.stream().collect(Collectors.toMap(Function.identity(), b -> 
            groupedSamples.getOrDefault(b, Collections.emptyList())));
}

This function first groups your samples, then based on your interested bins, either extract list out of the map, or create a empty list if it's absent.

How about concat lists and then create single parallel stream?

private Map<Integer, Collection<Integer>> segregateSamples(List<Double> sampleBins, List<Double> samples) {
    Stream<Double> d = Stream.concat(sampleBins.stream(), samples.stream()).parallel();
    return d.collect(Collectors.groupingByConcurrent(p -> (p.intValue() / 10),
                    Collectors.mapping(Double::intValue, Collectors.toCollection(ArrayDeque::new))));
}

The only disadvantage is that every collection contains its key.

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