16

I am curious how I can more effectively instantiate a dictionary in Java. At present I have passable code, yet I am filling it with data in a very obfuscated fashion.

Is there any way for me to initialize my dictionary similar to this? This is python for the record:

westernCanadaAdjList =  {  'BC': ['AB'],
                       'AB': ['BC', 'SK'],
                       'SK': ['AB', 'MB'],
                       'MB': ['SK']
                        }

I find for presentation purposes that is a whole lot more clear.

My current code in Java:

public class Main {

public static void main(String[] args) {

    //Adjacency List representation through a dictionary. Allows fast O(1) lookup time.
    Map<String,ArrayList<String>> adjList = new HashMap<String,ArrayList<String>>();


    //Adding values for Edmonton
    adjList.put("Edmonton", new ArrayList<String>());
    adjList.get("Edmonton").add("Neighbour1");
    adjList.get("Edmonton").add("Neighbour2");
    adjList.get("Edmonton").add("Neighbour3");


    //Adding values for Vancouver
    adjList.put("Vancouver", new ArrayList<String>());
    adjList.get("Vancouver").add("V neighbour1");
    adjList.get("Vancouver").add("V neighbour2");

    System.out.println(adjList.keySet() +" And Values " +  adjList.values());

    for (String neighbour: adjList.get("Edmonton")){
        System.out.println(neighbour);
    }

    for (String neighbour: adjList.get("Vancouver")){
        System.out.println(neighbour);
    }
  }
}

Thank you very much!

2
  • Maybe define your mapping as JSON in a text file, and read it with a parser like Jackson? Commented Jan 26, 2016 at 2:49
  • No, Java doesn't have collection literals like you're hoping for. Commented Jan 26, 2016 at 2:52

4 Answers 4

25

Note: The original answer is over 8 years old and Java has come a long way since then. As of now I'd recommend:

var map = Map.of(
    "BC", List.of("AB"),
    "AB", List.of("BC", "SK"),
    "SK", List.of("AB", "MB"),
    "MB", List.of("SK")
);

This is the best technique I know of:

    Map<String, String> myMap = new HashMap<String, String>() {{
        put("foo", "bar");
        put("key", "value");
        //etc
    }};

Note the double braces -- this is commonly called double brace initialization.

What you're actually doing is creating an anonymous inner class that extends HashMap, and your new subclass contains an initializer block, in which you can call any arbitrary code that is guaranteed to be executed before the instance can be used.

Also note the 'diamond operator' cannot be used with anonymous classes, for whatever reason.

This is a nice technique for test classes, but I tend to avoid it for production code.

EDIT: Thought I should answer your actual question!

double-brace initialization is probably the best solution in "pure" Java, your Map would specifically look like:

    Map<String, List<String>> westernCanadaAdjList = new HashMap<String, List<String>> () {{
        put("BC", new ArrayList<String>(){{ add("AB"); }});
        put("AB", new ArrayList<String>(){{ add("BC"); add("SK"); }});
        put("SK", new ArrayList<String>(){{ add("AB"); add("MB"); }});
        put("MB", new ArrayList<String>(){{ add("SK"); }});
    }};

... Still not super awesome. Java really does need a Map literal, and it does not have one.

For production code, I'd use a Guava's MultiMap, but honestly populating it with literals isn't much better:

    Multimap<String, String> multimap = ArrayListMultimap.create();
    multimap.put("BC", "AB");
    multimap.put("AB", "BC");
    multimap.put("AB", "SK");
    multimap.put("SK", "SK");
    multimap.put("SK", "SK");
    multimap.put("SK", "SK");
Sign up to request clarification or add additional context in comments.

1 Comment

I vote for guava. {{ }} creates an anonymous class, iirc, which is not great for performance
3

I recently faced a similar issue. I represented the data as a 2d array, relatively easy to type and parse, and wrote a utility method to parse it into the data structure. e.g. for your case

static String[][] CANADA_DATA = {
  {"BC"," AB"},
  {"AB","BC","SK"},
  // rest of Canada here
}

Example code

public Map<String, List<String>> parseIt() {

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

   for (String[] provinceData : CANADA_DATA ) {
      String name = provinceData [0];
      ArrayList neighbors = new ArrayList(Arrays.asList(provinceData ));
      neighbors.remove(0);  // remove ourself
      map.put(name, neighbors);
   } 

   return map; 
}

Obviously you can change the data format and parsing code to fit your specific needs.

Comments

2

I agree with Louis and didn't intend to add anything.

The use of streams in this case allows you to compact the code into one line but I realize this is not an answer to your question (just to closest I could think of).

        Map<String, List<String>> adjList = Stream.of(
            new SimpleEntry<>("Edmonton", Arrays.asList("E N1", "E N2", "E N3")), 
            new SimpleEntry<>("Vancouver", Arrays.asList("V N1", "V N2", "V N3")))
            .collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));

1 Comment

how did you deal with the type of e being Object and not SimpleEntry<>?
1

Yes, you can: Parse it as json:

import com.fasterxml.jackson.databind.ObjectMapper;

String json = "{'BC': ['AB']," +
              "'AB': ['BC', 'SK']," +
              "'SK': ['AB', 'MB']," +
              "'MB': ['SK']"
              "}";

Map<String, Object> map = new ObjectMapper().readValue(json, HashMap.class);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.