Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add to List value on HashMap

I'm populating a hashmap to have my objects grouped by one of their attributes. I find it “ugly” to check whether the list (the value) exists before adding my object to it.

An example will be more explicit:

// Need a map to group Person by age.
// Person = {age: int}
Map<Integer, List<Person>> myHashMap = new HashMap();

for (Person person : persons) {
   int age = person.getAge();
   List<Person> personsOfSameAge = myHashMap.get(age);
   if (personsOfSameAge != null) {
       personsOfSameAge.add(person);
   } else {
       personsOfSameAge = new ArrayList();
       personsOfSameAge.add(person);
       myHashMap.put(age, personsOfSameAge);
   }
}

Is there a better way to code this?

myHashMap.addToValueListOrCreateValueList(myObject);
like image 233
Fundhor Avatar asked Feb 01 '18 13:02

Fundhor


4 Answers

In Java 8, your whole code could be written like that (as other fine answers proposed):

Map<Integer, List<Person>> myHashMap = new HashMap();

for (Person person : persons) {
   myHashMap.computeIfAbsent(age,age->new ArrayList<Person>()).add(person);
}

But you could be even shorter by using a stream that collects into a Map with Collectors.groupingBy() :

Map<Integer, List<Person>> myMap = persons.stream().collect(Collectors.groupingBy(Person:getAge));

As a side note, your actual Java 7 code could also be improved. Of course, not as much as with Java 8 but if you cannot use Java 8, this may be interesting.
In your actual code, this is duplicated :

personsOfSameAge.add(person);

And you use two conditional statements (if and else) while only if would be enough if you handle first the special case : no value in the Map .
Here is a modified version :

Map<Integer, List<Person>> myHashMap = new HashMap<>();

for (Person person : persons) {
   int age = person.getAge();
   List<Person> personsOfSameAge = myHashMap.get(age);
   if (personsOfSameAge == null) {
       personsOfSameAge = new ArrayList();
       myHashMap.put(age, personsOfSameAge);
   }          
   personsOfSameAge.add(person);                
}
like image 74
davidxxx Avatar answered Nov 01 '22 06:11

davidxxx


Yes, there is in Java 8:

List<Person> personsOfSameAge = myHashMap.computeIfAbsent(age,age->new ArrayList<Person>());
personsOfSameAge.add(person);

Or just

myHashMap.computeIfAbsent(age,age->new ArrayList<Person>()).add(person);
like image 45
Eran Avatar answered Nov 01 '22 05:11

Eran


Something like this ?

  List<Person> newList = Optional.ofNullable( myHashMap.get(age)).orElse(new ArrayList<>());
  newList.add(person);
  myHashMap.put(age, newList);
like image 1
Suresh Atta Avatar answered Nov 01 '22 05:11

Suresh Atta


Though the question involves doing it in Java 8, I'm posting this as an alternate way.

You can use MultiMap from Google Guava.

Multimap<String, Person> myHashMap = ArrayListMultimap.create();
for (Person person : persons) {
   myHashMap.put(person.getAge(), person);
}

However, you have to be aware of a difference: Java's map returns null for a non-existent key while the Google Guava's MultiMap returns an empty List.

From javadoc for get(key) of MultiMap

Returns a view collection of the values associated with key in this multimap, if any. Note that when containsKey(key) is false, this returns an empty collection, not null. Changes to the returned collection will update the underlying multimap, and vice versa

like image 1
user7 Avatar answered Nov 01 '22 04:11

user7