Initializing a dictionary with a specific set of data cleanly in Java - java

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!

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");

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.

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()));

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);

Related

Nested Double Brace Initialization with HashMap and List

I'm trying to control some permissions on my App.
Yesterday I learn how to created Double Brace Initialization, It helped a lot. But now I'm trying to use it nested, but I'm getting a
')' expected
from the IDE (Android Studio)
Here is my code:
public static final Map<String, List> ALL_PERMISSIONS = new HashMap<String, List>() {{
put("Change-maps", new ArrayList<Integer>(){{add(R.id.button_change_view);}};);
put("Stores-info-view", new ArrayList<Integer>(){{add(R.id.details_fragment);}};);
put("Competitors-layer", new ArrayList<Integer>(){{add(R.id.switch_concorrentes);}};);
}};
am I missing something in it?
is that a bad approach?
PS: I'm trying this approach because in the future I'll use some keys with more than one View (Integer), and some keys with a list of String.
You should format/indent your code (Ctrl-Shift-F by default in Eclipse).
You'd see that your anonymous ArrayList class declaration (outside set of curly brackets) cannot be followed by a semi-colon.
Here's a formatted example that will work:
public static final Map<String, List> ALL_PERMISSIONS = new HashMap<String, List>() {
{
put("Change-maps", new ArrayList<Integer>() {
{
add(R.id.button_change_view);
}
});
put("Stores-info-view", new ArrayList<Integer>() {
{
add(R.id.details_fragment);
}
});
put("Competitors-layer", new ArrayList<Integer>() {
{
add(R.id.switch_concorrentes);
}
});
}
};
Note
Also mind the raw types or suppress the warnings.
If you look at this code:
Map<String, String> map = new HashMap<String, String>();
map.put( "string1", "string2" );
You can notice that the objects you are passing in parameters are not followed by a ;.
In your case, the second object you are passing is this one:
new ArrayList<Integer>(){{add(R.id.button_change_view);}}
So, you don't need the ; before your put's closing parenthesis, like this :
public static final Map<String, List> ALL_PERMISSIONS = new HashMap<String, List>() {{
put("Change-maps", new ArrayList<Integer>(){{add(R.id.button_change_view);}});
put("Stores-info-view", new ArrayList<Integer>(){{add(R.id.details_fragment);}});
put("Competitors-layer", new ArrayList<Integer>(){{add(R.id.switch_concorrentes);}});
}};
I would not encourage the use of double brace initilization. As this answer explains, it may
surprises your colleagues and is hard to read
harms performance
may cause problems with object equality (each object created has a
unique class object).
I would suggest, if possible, to use Guava ImmutableMap and ImmutableList
for example:
public static final Map<String, List> ALL_PERMISSIONS = ImmutableMap.<String, List>of(
"Change-maps", ImmutableList.of(R.id.button_change_view),
"Stores-info-view", ImmutableList.of(R.id.details_fragment),
"Competitors-layer", ImmutableList.of(R.id.switch_concorrentes)
);
or if you need to add more elements:
public static final Map<String, List> ALL_PERMISSIONS = new ImmutableMap.Builder<String, List>()
.put("Change-maps", ImmutableList.of(R.id.button_change_view))
.put("Stores-info-view", ImmutableList.of(R.id.details_fragment))
.put("Competitors-layer", ImmutableList.of(R.id.switch_concorrentes))
//(... and so on...
.build();

Concatenating two hashmaps without removing common entires from both the maps

I have two hashmaps, in particular vocabs of two languages say english and german.I would like to concatenate both these map to return a single map.I tried :
hashmap.putall()
But, removed some of the entries which are common in both maps and replace it by single entry only.But i want to keep both the vocabs intact just concatenate those. Is there any method to do it? if not any other way to do. I would prefer any methods in hashmap.
[EDIT]
To make more clear, lets see two maps
at the 500 um die 500
0 1 2 0 1 2
resutls into
at the 500 um die 500
0 1 2 3 4 5
You'll have to write your own custom "putAll()` method then. Something like this would work:
HashMap<String> both = new HashMap<String>(english);
for(String key : german.keySet()) {
if(english.containsKey(key)) {
both.put(key, english.get(key)+german.get(key));
}
}
This first copies the English HashMap. Then puts in all the German words, concatenating if there is a duplicate key. You might want some kind of separator character like a / in between so you can later extract the two.
There isn't anything like that in the Java main library itself, you will have to use something provided by third parties like Google Guava's Multimap, it does exactly what you want, or build something like this manually.
You can download the Guava library at the project's website. Using a multimap is the same as using a map, as in:
Multimap<String,String> both = new ArrayListMultimap <String,String>();
both.putAll( german );
both.putAll( english);
for ( Entry<String,String> entry : both.entrySet() ) {
System.out.printf( "%s -> %s%n", entry.getKey(), entry.getValue() );
}
This code will print all key-value pairs including the ones that are present on both maps. So, if you have me->me at both german and english they would be printed twice.
You cannot do that directly with any Map implementation, since in a map, each key is unique.
A possible workaround is to use Map<Key, List<Value>>, and then do the concatenation of your maps manually. The advantage of using a List for the concatenated map, is that it will be easy to retrieve each of the individual values without any extra fiddling.
Something like that would work:
public Map<Key, List<Value>> concat(Map<Key, Value> first, Map<Key, Value> second){
Map<Key, List<Value>> concat = new HashMap<Key, List<Value>>();
putMulti(first, concat);
putMulti(second, concat);
return concat;
}
private void putMulti(Map<Key, Value> content, Map<Key, List<Value>> dest){
for(Map.Entry<Key, Value> entry : content){
List<Value> vals = dest.get(entry.getKey());
if(vals == null){
vals = new ArrayList<Value>();
dest.put(entry.getKey(), vals);
}
vals.add(entry.getValue());
}
}
Similar to #tskuzzy's answer
Map<String, String> both = new HashMap<String, String>();
both.putAll(german);
both.putAll(english);
for (String e : english.keySet())
if (german.containsKey(e))
both.put(e, english.get(e) + german.get(e));
Slight improvisation of #tskuzzy and #Peter's answer here. Just define your own StrangeHashMap by extending HashMap.
public class StrangeHashMap extends HashMap<String, String> {
#Override
public String put(String key, String value) {
if(this.containsKey(key)) {
return super.put(key, super.get(key) + value);
} else {
return super.put(key, value);
}
}
}
You can use it as so:
Map<String, String> map1 = new HashMap<String, String>();
map1.put("key1", "Value1");
map1.put("key2", "Value2");
Map<String, String> map2 = new HashMap<String, String>();
map2.put("key1", "Value2");
map2.put("key3", "Value3");
Map<String, String> all = new StrangeHashMap();
all.putAll(map1);
all.putAll(map2);
System.out.println(all);
The above prints the below for me:
{key3=Value3, key2=Value2, key1=Value1Value2}
Given the new elements in the question, it seems that what you actually need to use is lists. In this case, you can just do:
List<String> english = ...;
List<String> german = ...;
List<String> concat = new ArrayList<String>(english.size() + german.size());
concat.addAll(english);
concat.addAll(german);
And there you are. You can still use concat.get(n) to retreive the value nth value in the concatenated list.

Array/list of hashmap?

I am coming from the world of Perl programming and am unfamiliar with how one would create a list of hashes in Java.
In perl, creating a list of hashes is easy.
#rows = (
{ food=>'pizza'},
{ drink=>'coke'}
);
Reading it out is just as easy:
foreach my $row (#$rows){
print $row->{food};
print $row->{drink};
}
How would one accomplish something similar in Java? Either with just strings, or also with the possibility of objects as well?
The following is roughly equivalent to your Perl code.
List<Map<String,String>> mapList = new ArrayList<Map<String,String>>();
Map<String,String>> map1 = new HashMap<String,String>();
map1.put("food", "pizza");
Map<String,String>> map2 = new HashMap<String,String>();
map2.put("drink", "coke");
Collections.addAll(mapList, map1, map2);
...
for (Map<String,String> map : mapList) {
System.out.println("food is " + map.get("food"));
System.out.println("drink is " + map.get("drink"));
}
However, as you can see this is a lot more cumbersome than in Perl. Which brings me to the point that it is usually a better idea to do this kind of thing in Java using custom classes instead of associative arrays (e.g. Map instances). Then you can write this as something like:
List<Diet> dietList = new ArrayList<Diet>();
Collections.addAll(dietList, new Diet("pizza", null), new Diet(null, "coke");
...
for (Diet diet : dietList) {
System.out.println("food is " + diet.getFood());
System.out.println("drink is " + diet.getDrink());
}
This approach (using a custom class) is generally more robust, more efficient, and gives you more readable code.
List<Map<String,String>> mapList = new ArrayList<HashMap<String,String>>()
Without code you've tried or more specification I can just provide an example of what you're
looking for. Ask more specifics and I can provide more info.
To iterate over the list:
for (Map<String, String> map : mapList) {
String value = map.get("food");
}
Actually in java is quite long compared to perl :(
To create:
List<Map<String,String>> list = new ArrayList<Map<String,String>>();
Map value1 = new HashMap();
value1.put("foo", "pizza");
list.add(value1);
Map value2 = new HashMap();
value2.put("drink", "coke");
list.add(value2);
To read:
for (Map<String,String> element : list) {
// print keys and values
}
If you want something more dynamic and still have access to java libraries you can use something like groovy (or scala or clojure).

Two-dimensional ArrayList

Just a very small question... I seem to run into too much complexity here: I have to realize an index-structure like {42, someString}. I tried:
Object entry[][] = new Object[1][1];
ArrayList<Object> my_list = new ArrayList<Object>();
However that looks really strange. Isn't there a better much simpler solution to just store some Integer and a String? I need to perfrom search for the Strings and return the Integer... so I thought Collections and ArrayLists are good friends in the Java API.
Solution: use a Map
Uhm, do you perhaps need a Map?
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("Some String", 42);
// or, more correctly:
map.put("Some String", Integer.valueOf(42));
You can search it using
Integer result = map.get("Some String");
Reference: Sun Java Tutorial > Collection Trail > Interfaces > The Map Interface
Fixing the OP's Code
BTW, the code in the question is flawed. Here's how you would do it if you wanted to use a List of object arrays (which you shouldn't):
// single dimension, not multi-dimension
Object[] entry = new Object[]{"Some String",Integer.valueOf(42)};
// use interface as variable, not implementation type
// generic type is object array, not object
List<Object[]> myList = new ArrayList<Object[]>();
// add array to list
myList.add(entry);
Now you could search like this:
for(final Object[] candidate : myList){
if("Some String".equals(candidate[0])){
System.out.println("Result: " + candidate[1]);
break;
}
}
However, this is just for reference, don't do it this way. The Collections Framework contains solutions for almost all standard cases. Use a Map.
Make a tuple class
public Class IntegerStringTuple {
private Integer number;
private String string;
//setters and getters etc.
}
If I understand correctly you should use a Map.
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(42, "someString");
String str = map.get(42);
Simply use a HashMap
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("foo",42);
why not use a map?
Map<String,Object>
It sounds like you want a Map
I would use a Map. Maps are used to store key value pairs.
Map<String, Integer> map = new HashMap<String, Integer>();
Map may not be used instead of an ArrayList when you require the order to be maintained.
ArrayList arr1 = new ArrayList();
ArrayList arr2 = new ArrayList();
arr2.add(1);
arr2.add(2);
arr2.add(3);
arr1.add(arr2);
for(int i=0;i<arr1.size();i++){
System.out.println("i:"+arr1.get(i));
for(int j=0;j<((ArrayList)arr1.get(i)).size();j++){
System.out.println("j:"+((ArrayList)arr1.get(i)).get(j));
}
}
output: i:[1, 2, 3]
j:1
j:2
j:3
ArrayList<String> lcname = new ArrayList<String>();
lcname.add(cname);
ArrayList<String> lsize = new ArrayList<String>();
lsize.add(size);
Dictionary dictionary = new Hashtable();
Hashtable<String, ArrayList<ArrayList>> hashtable =
new Hashtable<String, ArrayList<ArrayList>>();
hashtable.put(fname, new ArrayList<>());
hashtable.get(fname).add(lcname);
hashtable.get(fname).add(lsize);
System.out.println(hashtable);
Here is the code for dictionaries of list(list).
OUTPUT
{file name=[[column name], [size]]}

converting static 2D String array to HashMap

What is the easiest way to convert a 2D array of Strings into a HashMap?
For example, take this:
final String[][] sheetMap = { /* XSD Name, XSL Sheet Name */
{"FileHeader", "FileHeader"},
{"AccountRecord", "AccountRecord"},
{"DriverCardRecord", "DriverCardRecord"},
{"AssetCardRecord", "AssetCardRecord"},
{"SiteCardRecord", "SiteCardRecord"}
};
This is most likely going to be loaded from a file and will be much bigger.
final Map<String, String> map = new HashMap<String, String>(sheetMap.length);
for (String[] mapping : sheetMap)
{
map.put(mapping[0], mapping[1]);
}
If you just want to initialize your map in a convenient way, you can use double brace initialization:
Map<String, String > sheetMap = new HashMap<String, String >() {{
put( "FileHeader", "FileHeader" );
put( "AccountRecord", "AccountRecord" );
put( "DriverCardRecord", "DriverCardRecord" );
put( "AssetCardRecord", "AssetCardRecord" );
put( "SiteCardRecord", "SiteCardRecord" );
}};
As a slightly cleaner alternative to tradeJmark answer:
String[][] arr = // your two dimensional array
Map<String, String> arrMap = Arrays.stream(arr).collect(Collectors.toMap(e -> e[0], e -> e[1]));
// Sanity check!
for (Entry<String, String> e : arrMap.entrySet()) {
System.out.println(e.getKey() + " : " + e.getValue());
}
Wait; if this is going to be loaded from a file, don't go through the intermediate array step! You would have had to load it all first before creating the array or you wouldn't know the size for the array. Just create a HashMap and add each entry as you read it.
The existing answers work well, of course, but in the interest of continually updating this site with new info, here's a way to do it in Java 8:
String[][] arr = {{"key", "val"}, {"key2", "val2"}};
HashMap<String, String> map = Arrays.stream(arr)
.collect(HashMap<String, String>::new,
(mp, ar) -> mp.put(ar[0], ar[1]),
HashMap<String, String>::putAll);
Java 8 Streams are awesome, and I encourage you to look them up for more detailed info, but here are the basics for this particular operation:
Arrays.stream will get a Stream<String[]> to work with.
collect takes your Stream and reduces it down to a single object that collects all of the members. It takes three functions. The first function, the supplier, generates a new instance of an object that collects the members, so in our case, just the standard method to create a HashMap. The second function, the accumulator, defines how to include a member of the Stream into the target object, in your case we simply want to put the key and value, defined as the first and second value from each array, into the map. The third function, the combiner, is one that can combine two of the target objects, in case, for whatever reason, the JVM decided to perform the accumulation step with multiple HashMaps (in this case, or whatever other target object in another case) and then needs to combine them into one, which is primarily for asynchronous execution, although that will not typically happen.
More concise with streams would be:
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
import java.util.Map;
...
public static Map<String, String> asMap(String[][] data) {
return stream(data).collect(toMap( m->m[0], m->m[1] ));
}
...
Java 8 way
public static Map<String, String> convert2DArrayToMap(String[][] data){
return Arrays.stream(data).collect(Collectors.toMap(m -> m[0], m -> m[1]));
}
with loop
public static Map<String, String> convert2DArrayToMap(String[][] data)
{
Map<String, String> map = new HashMap<>();
for (String[] m : data)
{
if (map.put(m[0], m[1]) != null)
{
throw new IllegalStateException("Duplicate key");
}
}
return map;
}

Categories

Resources