1
votes

I have Multimap. Example:

00254=[00255, 2074E, 2074E, 2074E, 2074E, 2074E, 2074E, 00010, 00010, 00010, 0006, 0006, 0006, 00010, R01018, R01018, 0006, 0006, R01018, R01018, R01018, 12062, S2202962, S2202962, R01018, 12062, 20466, 12062, 20466, 22636, 20466, 20466, 22636, 22636, 22636, 22636, 22636, 22636, 22636, 22636, 00255, 2074E, 2074E, 2074E, 2074E, 2074E]
00256=[00257, 2074E, 2074E, 2074E, 2074E, 00010, 2074E, 2074E, 0006, 00010, 00010, 00010, 0006, 0006, 0006, R01018, R01018, 0006, R01018, R01018, R01018, 12062, S2202962, S2202962, R01018, 12062, 20466, 12062, 20466, 20466, 20466, 22636, 22636, 22636, 22636, 22636, 22636, 22636, 22636, 22636, 00257, 2074E, 2074E, 2074E, 2074E, 00010]

I want to get the number of value including duplicate value.

  • 00254=[00255:2, 2074E:11, 00010:4, 0006:5, R01018:6, ...]
  • 00256=[00257:2, 2074E:10, 00010:5, 0006:5, R01018:7, ...]

Is it possible to get duplicate number?

Thanks.

2
Everything is possible. Have you tried something, do you have something to show us ? And of course, can you explain what you try to check exactly. You speak about number value but I see R01018 ... not a 10-based number. - AxelH
Thanks AxelH. I want to count the number of values. The type of data is not important. Any value can be. - torochi
Do you mean you want the count of values for a key, e.g. for key 00254 the count would be the sum of 2+11+4+5+6+...? - Andreas
Per key, iterate the value and use a Map<YourValue, Integer> to count. But you need to try first. That "value" is nothing but a List, so you can simply use the same logic. See Count occurrences of words in ArrayList - AxelH
Did you read the documentation, i.e. the javadoc of Multimap? Perhaps get(key).size() is what you're looking for? - Andreas

2 Answers

4
votes

Solution using Java 8 Stream to get the occurrence for a specific value, simply get the Collection for a value, then group on the value and count (using the Collectors function) to get a Map<String, Long> :

Multimap<Integer, String> maps =  ArrayListMultimap.create();
maps.put(1, "foo");
maps.put(1, "bar");
maps.put(1, "foo");
maps.put(2, "Hello");
maps.put(2, "foo");
maps.put(2, "World");
maps.put(2, "World");

Here is the idea to print the occurences per value :

maps.keySet().stream() //Iterate the `keys`
            .map(i -> i + " : " +  //For each key
                        maps.get(i).stream() //stream the values.
                            .collect( //Group and count
                                    Collectors.groupingBy(
                                            Function.identity(), 
                                            Collectors.counting()
                                    )
                            )
            )
            .forEach(System.out::println);

1 : {bar=1, foo=2}

2 : {Hello=1, foo=1, World=2}

This will generate a String, I let you adapt for your need.

3
votes

Counting occurrences is a perfect use case for Multiset, which is

A collection that supports order-independent equality, like Set, but may have duplicate elements. A multiset is also sometimes called a bag.

Elements of a multiset that are equal to one another are referred to as occurrences of the same single element.

There are few different Multiset implementations you can choose from, and couple of handy set-like methods in Multisets helper class.

Here, you could either 1) collect to multiset or 2) use custom multimap with multiset values.

  1. Instead of grouping to map, you can collect an immutable multiset:

    ImmutableMultiset<String> qtyMultiset = multimap.get(key).stream()
        .collect(ImmutableMultiset.toImmutableMultiset());
    

    or mutable one:

    HashMultiset<String> qtyMultiset = multimap.get(key).stream()
        .collect(Multisets.toMultiset(Function.identity(), e -> 1, HashMultiset::create));
    
  2. Or maybe you can use custom multimap in the first place? (Unfortunately, there isn't any MultisetMultimap interface nor implementation, hence need for custom instance):

    Multimap<String, String> countingMultimap
        = Multimaps.newMultimap(new LinkedHashMap<>(), LinkedHashMultiset::create);
    

    You can drop Linked part if you don't need to preserve order. For your data:

    countingMultimap.putAll("00254", ImmutableList.of("00255", "2074E", "2074E", "2074E", "2074E", "2074E", "2074E", "00010", "00010", "00010", "0006", "0006", "0006", "00010", "R01018", "R01018", "0006", "0006", "R01018", "R01018", "R01018", "12062", "S2202962", "S2202962", "R01018", "12062", "20466", "12062", "20466", "22636", "20466", "20466", "22636", "22636", "22636", "22636", "22636", "22636", "22636", "22636", "00255", "2074E", "2074E", "2074E", "2074E", "2074E"));
    countingMultimap.putAll("00256", ImmutableList.of("00257", "2074E", "2074E", "2074E", "2074E", "00010", "2074E", "2074E", "0006", "00010", "00010", "00010", "0006", "0006", "0006", "R01018", "R01018", "0006", "R01018", "R01018", "R01018", "12062", "S2202962", "S2202962", "R01018", "12062", "20466", "12062", "20466", "20466", "20466", "22636", "22636", "22636", "22636", "22636", "22636", "22636", "22636", "22636", "00257", "2074E", "2074E", "2074E", "2074E", "00010"));
    

    returned multimap would be:

    {00254=[00255 x 2, 2074E x 11, 00010 x 4, 0006 x 5, R01018 x 6, 12062 x 3, S2202962 x 2, 20466 x 4, 22636 x 9], 00256=[00257 x 2, 2074E x 10, 00010 x 5, 0006 x 5, R01018 x 6, 12062 x 3, S2202962 x 2, 20466 x 4, 22636 x 9]}
    

For more information read Guava Wiki page about Multiset (which uses word count as an example).