Hot questions for Operating Hashmap in Guava

Top 10 Java Open Source / Guava / Operating Hashmap

builder for HashMap

Question: Guava provides us with great factory methods for Java types, such as Maps.newHashMap().

But are there also builders for java Maps?

HashMap<String,Integer> m = Maps.BuildHashMap.
    put("a",1).
    put("b",2).
    build();

Answer: Since Java 9 Map interface contains:

  1. Map.of(k1,v1, k2,v2, ..)
  2. Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ..)

Limitations of those factory methods are that they:

  1. can't hold nulls as keys and/or values
  2. produce immutable maps

If we need mutable map (like HashMap) we can use its copy-constructor and let it copy content of map created via Map.of(..)

Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );

ImmutableMap.of() workaround for HashMap in Maps?

Question: There are utility methods to create ImmutableMap like Immutable.of(Key, value) and its overload.

But such methods don't exist for HashMap or LinkedHashMap in Maps class.

Is there any better way to do this or Guava assumes such a map is always a constant map and ImmutableMap is best option to go with and don't need to provide a utility for HashMap.

Answer: Why would you want those for a regular HashMap or LinkedHashMap? You can just do this:

Map<String, Object> map = Maps.newHashMap();
map.put(key, value);

The thing with ImmutableMap is that it is a little bit more cumbersome to create; you first need to make a Builder, then put the key-value pairs in the builder and then call build() on it to create your ImmutableMap. The ImmutableMap.of() method makes it shorter to write if you want to create an ImmutableMap with a single key-value pair.

Consider what you'd have to write if you wouldn't use the ImmutableMap.of() method:

ImmutableMap<String, Object> map = ImmutableMap.builder()
    .put(key, value);
    .build();

What is difference between HashMap and HashMultimap

Question: I see many examples about multimap but did not understand why Google Gauva is different?

Multimap<Integer, Set<String>> option4 = HashMultimap.create(); // Gauva

Map<Integer, Set<String>> opt = new HashMap<Integer, Set<String>>(); //Core Java

Is both above are behave same for holding data or different?

Answer: A MultiMap<A, B> associates a key of type A with a value of type Collection<B> (hence the name MultiMap)

A Map<A, B> associates a key of type A with a value of type B.

So, a MultiMap<Integer, Set<String>> can be viewed as a Map<Integer, Collection<Set<String>>.


adding a key to HashMap without the value?

Question: Is there a way to add a key to a HashMap without also adding a value? I know it seems strange, but I have a HashMap<String, ArrayList<Object>> amd I want to first be able to create keys as needed and then check if a certain key exists and, if so, put the appropriate value, namely the ArrayList<Object>

Was that confusing enough?

Answer: Since you're using a Map<String, List<Object>>, you're really looking for a multimap. I highly recommend using a third-party library such as Google Guava for this - see Guava's Multimaps.

Multimap<String, Object> myMultimap = ArrayListMultimap.create();

// fill it
myMultimap.put("hello", "hola");
myMultimap.put("hello", "buongiorno");
myMultimap.put("hello", "สวัสดี");

// retrieve
List<String> greetings = myMultimap.get("hello");
                      // ["hola", "buongiorno", "สวัสดี"]

Java 8 update: I'm no longer convinced that every Map<K, SomeCollection<V>> should be rewritten as a multimap. These days it's quite easy to get what you need without Guava

Map<String, List<Object>> myMap = new HashMap<>();

// fill it
myMap.computeIfAbsent("hello", ignored -> new ArrayList<>())
  .addAll(Arrays.asList("hola", "buongiorno", "สวัสดี");

// retrieve
List<String> greetings = myMap.get("hello");
                      // ["hola", "buongiorno", "สวัสดี"]

HashMap returned by Maps.newHashMap vs new HashMap

Question: I am trying Guava for the first time and I find it really awesome.

I am executing few parameterized retrieve queries on a Spring jdbc template. The method in the DAO (AbstractDataAccessObject) goes like this. No problem here.

public Map<String,Object> getResultAsMap(String sql, Map<String,Object> parameters) {
    try {
        return jdbcTemplate.queryForMap(sql, parameters);
    } catch (EmptyResultDataAccessException e) {
        //Ignore if no data found for this query
        logger.error(e.getMessage(), e);

    }
    return null;
}

Here's the problem :

When I call this method using

getResultAsMap(query, new HashMap<String,Object>(ImmutableMap.of("gciList",gciList)));

it works great.

But when I do this

getResultAsMap(query, Maps.newHashMap(ImmutableMap.of("gciList",gciList)));

the compiler gets upset saying

The method getResultAsMap(String, Map<String,Object>) in the type AbstractDataAccessObject is not applicable for the arguments (String, HashMap<String,List<String>>)

Am I doing something wrong or what could be the reason for this complaint?

Answer: This is type inference failing. Maps.newHashMap is a static parameterized method. It allows you to use

Map<String,Integer> map = Maps.newHashMap()

instead of

Map<String,Integer> map = new HashMap<String,Integer>()

saving you from having to type <String,Integer> twice. In Java 7, the diamond operator allows you to use

Map<String,Integer> map = new HashMap<>()

so the method is then redundant.

To answer your question, just use the new HashMap version, since type inference doesn't work for method parameters. (You could use Maps.<String,Object>newHashMap() but that defeats the point of using the method)


HashMap<String, boolean> copy all the keys into HashMap<String, Integer>and initialize values to zero

Question: What is the best way ?

Just looping through and putting the key and zero, or is there another more elegant or existing library method. I am also using Google's guava java library if that has any useful functionality ?

Wanted to check if there was anything similar to the copy method for lists, or Map's putAll method, but just for keys.

Answer: Don't think there's much need for anything fancy here:

Map<String, Boolean> map = ...;
Map<String, Integer> newMap = Maps.newHashMapWithExpectedSize(map.size());
for (String key : map.keySet()) {
  newMap.put(key, 0);
}

If you do want something fancy with Guava, there is this option:

Map<String, Integer> newMap = Maps.newHashMap(
    Maps.transformValues(map, Functions.constant(0)));

// 1-liner with static imports!
Map<String, Integer> newMap = newHashMap(transformValues(map, constant(0)));