In Java, can I declare a HashMap constant?

46,536

Solution 1

There are two aspects here:

  • You have a variable that holds a reference to a map. You simply declare that variable to be final; and voilà, you can't change the variable to point to another reference.
  • But that doesn't prevent you from changing the state of the object that reference is pointing. So even when you have a final Map object, you could still call put/remove/... methods on that Map.

So, in order to come to a real "constant" Map, you do go for this:

First you create an ordinary map containing all the desired values.

Then you use Collections.unmodifiableMap() in order to create a map that can't be changed any more.

The one thing to pay attention to: you have to make sure that you are not leaking a reference to the original map object as that immutable map is just a wrapper around that initial map. You could do that in a little helper method for example.

If you are asking: is there a way to write down a map declaration in a special "literal form" (like for arrays) - there isn't. Well, you can change that helper method to take Object ... as parameter; and then you would go through those parameters, one is a number, next one a string, and use that to fill the map. So you could then invoke the method with a single sequence of values. But probably not worth the effort ...

A quick update: Java9 added various static helper methods that allow you write down maps in a somehow "literal" way, see here for example.

Solution 2

Yes, it can be a constant. You should declare your HashMap instance as follows:

class <class name> {
    private static final HashMap<Integer, String> tensNumberConversion = new HashMap<>();

    static {
        tensNumberConversion.put(2, "twenty");
        tensNumberConversion.put(3, "thirty");
        tensNumberConversion.put(4, "forty");
        tensNumberConversion.put(5, "fifty");
        tensNumberConversion.put(6, "sixty");
        tensNumberConversion.put(7, "seventy");
        tensNumberConversion.put(8, "eighty");
        tensNumberConversion.put(9, "ninety");
    }
}

However, this is only a constant reference. While you can not reassign tensNumberConversion to something else, you still can change the contents of your map later at runtime:

tensNumberConversion = new HashMap<>(); // won't compile
tensNumberConversion.put(9, "one"); // succeeds

If you want the contents of your map constant too, you should wrap the HashMap into an unmodifiable map:

class <class name> {
    private static final Map<Integer, String> tensNumberConversion = initMap();

    private static Map<Integer, String> initMap() {
        Map<Integer, String> map = new HashMap<>();
        map.put(2, "twenty");
        map.put(3, "thirty");
        map.put(4, "forty");
        map.put(5, "fifty");
        map.put(6, "sixty");
        map.put(7, "seventy");
        map.put(8, "eighty");
        map.put(9, "ninety");
        return Collections.unmodifiableMap(map);
    }
}

Solution 3

Just a BTW, with Java 9 there are static methods in the Map interface to create immutable Maps with some number of entries like:

Map<String,String> m = Map.of("k1", "v1",
                              "K2", "v2");

Or if more than 10 entries are needed:

import static java.util.Map.entry;
Map<String, String> m = Map.ofEntries(
    entry("k1", "v1"), entry("k2", "v2"), …);

Solution 4

I somehow missed the easiest form for direct initialization of the HashMap here

private static final Map<Integer, String> tensNumberConversion_internal = new HashMap<Integer, String>()
{
    {
        put(2, "twenty");
        put(3, "thirty");
        put(4, "forty");
        put(5, "fifty");
        put(6, "sixty");
        put(7, "seventy");
        put(8, "eighty");
        put(9, "ninety");
    };
};

and as for making it unchangeable you already have Collections.unmodifiableMap from the accepted answer so do

public static final Map<Integer, String> tensNumberConversion = Collections.unmodifiableMap(tensNumberConversion_internal);

they have to be created in this order ofcourse ;)

Share:
46,536

Related videos on Youtube

Hoonta
Author by

Hoonta

Updated on July 09, 2022

Comments

  • Hoonta
    Hoonta almost 2 years

    I am writing a simple program to convert a number to a word representing that number (13 => "thirteen").

    I realize I could get some of the words with a constant String array like this:

    private static final String[] tensNames = {"", " ten", " twenty", " thirty", " forty", " fifty", " sixty", " seventy", " eighty", " ninety" };
    

    ...and access it with the index, but I wanted to try it with a HashMap like this:

    final HashMap<Integer, String> tensNumberConversion = new HashMap<Integer, String>();
        tensNumberConversion.put(2, "twenty");
        tensNumberConversion.put(3, "thirty");
        tensNumberConversion.put(4, "forty");
        tensNumberConversion.put(5, "fifty");
        tensNumberConversion.put(6, "sixty");
        tensNumberConversion.put(7, "seventy");
        tensNumberConversion.put(8, "eighty");
        tensNumberConversion.put(9, "ninety");
    

    I was instructed by a teacher to make these constants. Can the HashMap be a constant? As a newbie, it wasn't totally clear how the terms "constant" and "static" and "final" are related, and what exactly makes a constant. (static alone? final alone? static final together?).

    I tried making it private static final Hashmap but IntelliJ gave me an error (modifier 'private' not allowed here...same for 'static').

    However, it does compile from my terminal with no errors. If a HashMap can be a constant, is this the right way to declare one? Thanks!

    • Courage
      Courage about 7 years
      It can be a constant, where are you declaring your HashMap?
    • ZhekaKozlov
      ZhekaKozlov about 7 years
      Possible duplicate of How can I initialise a static Map?
    • Hoonta
      Hoonta almost 7 years
      According to what I see, I accepted your answer on May 12th. Is that different from what you see?
  • ZhekaKozlov
    ZhekaKozlov about 7 years
    Collections.unmodifiableMap
  • GhostCat
    GhostCat about 7 years
    Much better now.
  • Hulk
    Hulk about 7 years
    but that is basically what the map returned by Collections.unmodifiableMap already does - except that it throws instead of pretending to work - all modifying methods throw UnsupportedOperationException.
  • hansvb
    hansvb over 5 years
    I would not call that the "simplest" form, the double-brace initialization trick is quite a heavy hack: stackoverflow.com/questions/924285/…
  • nar-007
    nar-007 almost 4 years
    Anyway, map.of or map.ofEntry will maintain order?
  • Prashant
    Prashant almost 4 years
    I think it's only guaranteed for SortedMap implementations and LinkedHashMap. Map.of specifically mentions that the iteration order is unspecified. docs.oracle.com/javase/9/docs/api/java/util/Map.html#immutab‌​le