Matthew Anderson
Matthew Anderson

Reputation: 21

Java stream not putting all values of a List into a HashMap

I am working on a program where I need to take a list of objects and put them in a HashMap, using the class name as the key and the instance as the value.

I have the following code:

pets2.stream().forEach(pt -> animals.put(pt.getClass().getSimpleName(), pt));

Where pets2 is my list of animals is my HashMap. However, when I run this and then print the HashMap, only two objects from the list have been added into the Map.

Not even the first two, just two of them. Any idea as to what the issue is and how I can fix it?

Upvotes: 2

Views: 463

Answers (3)

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28988

If you have multiple instances of the same class in a list, you will lose information if only a single object will represent a value in your map.

Instead, you have to group the objects mapped to the same key (i.e. belonging to the same class) into a collection.

With stream API, you can do it by using the built-in collector Collectors.groupingBy(), which expects a classifier function that determines how to extract the key from the element of the stream. By default, values mapped to the same key will be grouped into a list.

Example (substitute the type Object with your super type):

List<Object> pets2 = List.of("foo", 9, "bar", 27, new HashSet<>());

Map<String, List<Object>> classNameToPet =
        pets2.stream()
             .collect(Collectors.groupingBy(obj -> obj.getClass().getSimpleName()));

classNameToPet.forEach((k, v) -> System.out.println(k + " : " + v));

Output

Integer : [9, 27]
String : [foo, bar]
HashSet : [[]]

Note :

  • The way you are using forEach() is not encouraged by the documentation, take a look at the code example provided in the paragraph "side-effects".

  • If you need to determine the actual class of your objects in order to interact with them, that means your usage of inheritance is wrong. If list pets2 has a generic parameter let's say Anymal, then you don't have to discriminate between the subtypes of Anymal, but take advantage from the polymorphism.

Upvotes: 3

avivadla8
avivadla8

Reputation: 61

When you are iterating over the pets2 list and adding them to the animals map (Map<String, Object>), the put operation just overwrites the already inserted value for that specific class.

Eg:

pets2 = [dog1, dog2, cat1, cat2]
// The final animals map as per your method will be
animals = [{Dog, dog2}, {Cat, cat2}]

So, to handle the case of multiple repetitions of the same class objects in the pets list, we can change the code as follows

Map<String, List<Object>> animals = new HashMap<>();
pets2.forEach(pet -> animals.computeIfAbsent(pet.getClass().getSimpleName(), p -> new ArrayList<>()).add(pet));

Upvotes: 0

Henry
Henry

Reputation: 43738

This would happen, if there are only two different classes in the list. Later objects with the same class as a previous on overwrite the previous one because the key is the same.

Upvotes: 0

Related Questions