4

I have the following data structure:

Map<Integer, Map<String,Double>>

----------------
|     | a    2 |
| 100 | b    1 |    
|     | c    2 |
----------------
|     | a    2 |
| 101 | c    2 |    
----------------
|     | a    2 |
| 102 | b    1 |    
|     | c    2 |
----------------

GOAL: get the ID of the outer map containing the inner map with the maximum size.

For example, 100 or 102, which both contain an inner map whose size is 3.

How can I use Stream API, for example?

Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
Fab
  • 1,145
  • 7
  • 20
  • 40
  • 1
    What attempt have you made so far? – khelwood Dec 19 '16 at 10:08
  • @khelwood I iterated over the outer map keys, saving the size of the inner map and the corresponding outer ID. But I have about 20000 keys: the process is very slow, so I need a faster way. – Fab Dec 19 '16 at 10:11
  • Not bad a start. I guess doing the lookup each time for all the keys is pretty timeconsuming. Are the inner Maps accessed indepenently or only through your outer map? I.E. do you have control over when the inner maps are added to /deleted from in a single place? If so, you could have a reference to the max and update it on each add/remove to an inner map ... a lot of "Ifs" still ... – Fildor Dec 19 '16 at 10:18

3 Answers3

5
map.values().stream().max(Comparator.comparingInt(Map::size)).get()

is what you're looking for.

map.keySet().stream().max(Comparator.comparingInt(k -> map.get(k).size()))

or the above if you want the key, not the map. Test code:

Map<Integer, Map<String, Integer>> map = new HashMap<>();
        Map<String, Integer> map2 = new HashMap<>();
        map2.put("A", 1);
        map2.put("B", 2);
        map2.put("C", 3);
        map.put(100, map2);
        map2 = new HashMap<>();
        map2.put("A", 1);
        map2.put("B", 2);
        map.put(101, map2);
        System.out.println(map.values()
                              .stream()
                              .max(Comparator.comparingInt(Map::size)).get());
xenteros
  • 15,586
  • 12
  • 56
  • 91
5

To get the key of the outer map of any entries that have the biggest map as value, the code could be:

int id = map.entrySet()
    .stream()
    .max(Map.Entry.comparingByValue(Comparator.comparingInt(Map::size)))
    .get() // May throw a NoSuchElementException if there is no max
    .getKey();

Another approach allowing to get the value as Optional<Integer> as proposed by @Holger which avoids to get a NoSuchElementException in case we have no max when the map is empty for example.

Optional<Integer> id = map.entrySet()
    .stream()
    .max(Map.Entry.comparingByValue(Comparator.comparingInt(Map::size)))
    .map(Map.Entry::getKey);

Please note, that those approaches are more efficient than going through the keys and calling map.get(k).size() to get the size of the inner map because you do unnecessary map lookups that you don't do when iterating over the entries directly as proposed by those approaches.

Community
  • 1
  • 1
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
  • 1
    You may also use `Map.Entry.comparingByValue(Comparator.comparingInt(Map::size))` as comparator. And if the presence of a maximum isn’t guaranteed (i.e. the map might be empty), replace `.get().getKey()` by `.map(Map.Entry::getKey)` to get an `Optional`. – Holger Dec 19 '16 at 15:43
4

This should give an output of 100 or the key with Maximum elements.

map.keySet().stream().max((v1, v2) -> {
                return Integer.compare(map.get(v1).size(), map.get(v2).size());
            })

or using Comparator

map.keySet().stream().max(Comparator.comparingInt(v -> map.get(v).size()))

Complete Code:

Map<Integer,Map<String,Double>> map = new HashMap<>();
Map<String,Double> map100 = new HashMap<>();
map100.put("a", 2.0);
map100.put("b", 1.0);
map100.put("c", 2.0);
Map<String,Double> map101 = new HashMap<>();
map101.put("a", 2.0);
map101.put("b", 1.0);
Map<String,Double> map102 = new HashMap<>();
map102.put("a", 2.0);
map102.put("b", 1.0);
map102.put("c", 2.0);
map102.put("d", 2.0);
map.put(100, map100);
map.put(101, map101);
map.put(102, map102);

System.out.println(map.keySet().stream().max(Comparator.comparingInt(v -> map.get(v).size())));

System.out.println(map.keySet().stream().max((v1, v2) -> {
    return Integer.compare(map.get(v1).size(), map.get(v2).size());
}));
Kishore Bandi
  • 5,537
  • 2
  • 31
  • 52
  • 2
    Note that this can produce tons of hash lookups for larger maps. If you know that you’ll need the keys *and* values, you should iterate over the `entrySet` in the first place instead of iterating over the `keySet` and doing a lookup per key. I.e. `map.entrySet().stream().max(Map.Entry.comparingByValue( Comparator.comparingInt(Map::size))).map(Map.Entry::getKey)` – Holger Dec 19 '16 at 15:27
  • Holger - Excellent suggestion, This indeed results in a lot of unnecessary lookups which can be avoided. @Nicolas gave a good example explaining this. – Kishore Bandi Dec 19 '16 at 16:19