Nested Double Brace Initialization with HashMap and List - java

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

Related

Will there still be aliasing after I run my copy methods?

I have a map with a map as values and a map with a set as values (in java). I wrote a method for each to copy them, and try to avoid aliasing, but by the way my program is behaving, I'm not sure they work.
private Map<String, Set<String>> deepCopySet(Map<String, Set<String>> ruledOutCount) {
Map<String,Set<String>> copy = new HashMap<String,Set<String>>();
for(Map.Entry<String, Set<String>> entry : ruledOutCount.entrySet())
{
copy.put(entry.getKey(), new HashSet<String>(entry.getValue()));
}
return copy;
}
private Map<SG, Map<classObj, Integer>> deepCopyMap(Map<SG, Map<classObj, Integer>> classCountPerSG)
{
Map<SG,Map<classObj,Integer>> copy = new HashMap<SG,Map<classObj,Integer>>();
for(Map.Entry<SG, Map<classObj,Integer>> entry : classCountPerSG.entrySet())
{
copy.put(entry.getKey(), new HashMap<classObj,Integer>(entry.getValue()));
}
return copy;
}
classObj and SG are my own objects.
Is there any aliasing possible after I run these copy methods?
Thanks.
The deepCopySet method looks fine.
The deepCopyMap method is also fine, but it depends on the types of SG and classObj: If it is possible that they are also maps (or other complex objects), you might get a shallow copy.
Be aware that Java does type checks only at compile time! So in the following code
StringBuilder willNotBeCopied = new StringBuilder();
Map evil = new HashMap();
evil.put("a", new HashSet(Arrays.asList(willNotBeCopied)));
Map shallowCopy = deepCopySet(evil);
willNotBeCopied.append("some text");
the shallowCopy map would contain the same StringBuilder instance (shallow copy).

Initializing a dictionary with a specific set of data cleanly in 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);

passing a map into another method to be modified in java

So I came across some code that I thought looked kind of strange. Wanted to see what some of your opinions are of this
public class Test {
public static void main(String[] args) {
HashMap m = new HashMap();
Test2 t2 = new Test2();
t2.fill(m);
}
}
public class Test2 {
public void fill(HashMap m) {
m.put(new Integer(0), new Integer(0));
}
}
So is this code OK or should it be done another way?
Thanks
This is perfectly fine since objects in java are passed by reference. If you try to assign to m directly within a method, it is wrong:
m = new HashMap();
But you can use the passed reference to modify the object passed as an argument as is the case with your sample code.
Think of it as passing the location of the object into the function. You can use this location information to fiddle with it. But since the location is just a value, assigning to the location (m) does not have an effect on m from where you call the function. That's why the article says the argument is passed by value.
Is it OK to pass a map to a method for that method to manipulate the map? Sure.
The map is untyped; should be Map<Integer,Integer>. Use the compiler to help you get things right. Using generic types will also allow auto-boxing to be used so you can do the more succinct put(0,0).
The map should be passed as a Map, not a HashMap unless HashMap is explicitly needed (which for the case of HashMap is not going to be the case). As much as possible, use the interface, not the implementation.
The name fill looks like it's a bad name to me - it doesn't seem to "fill" anything.
As an aside, I would recommend against the magic anonymous class initialize, done so:
Map<Integer, Integer> m = new HashMap<Integer, Integer>() {{
put(0, 0);
}};
in favor of a simple initializer block:
Map<Integer, Integer> m = new HashMap<Integer, Integer>(); {
m.put(0, 0);
}
which avoids creating a redundant anonymous inner class file of the form SomeClass$n.class.
I would do this:
Map<Integer, Integer> m = new HashMap<Integer, Integer>() {{
put(0, 0);
}};
Here's a breakdown of the java kung fu being used here:
The map is typed <Integer, Integer>
This is an anonymous class with an instance block to initialize the map
Note the use of put(0, 0) rather than m.put(new Integer(0), new Integer(0)), making use of auto-boxing

How to give the static value to HashMap?

I have HashMap like:
static HashMap<String,ArrayList<Media>> mediaListWithCategory=new HashMap<String,ArrayList<Media>>();
I have value like:
January:
-Sunday
-Monday
Februsry:
-Saturday
-Sunday
-Thursday
March:
-Monday
-Tuesday
-Wednesday
How can I statically assign these values when defining the hash map?
You can populate it in a static block:
static {
map.put("January", Arrays.asList(new Media("Sunday"), new Media("Monday")));
}
(You should prefer interface to concrete classes. define your type as Map<String, List<Media>>)
Use a static block:
static {
mediaListWithCategory.put(youKey, yourValue);
}
A variant of this may be more succinct:
static HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>() {{
put("January", new ArrayList<String>() {{
add("Sunday");
add("Monday");
}});
put("Februsry" /* sic. */, new ArrayList<String>() {{
add("Saturday");
add("Sunday");
add("Thursday");
}});
put("March", new ArrayList<String>() {{
add("Monday");
add("Tuesday");
add("Wednesday");
}});
}};
See Double Brace Initialisation for discussion.

Define a map as constant in java

For my Android app I've the need of defining some keys in a single constant, and I think the best way to do it is using a map. But not sure whether that's really the way to go, and how to do it correctly. As I'm targeting Android, a Bundle may also be an option.
I have a list of keys like:
"h" = "http"
"f" = "ftp"
Basically the program is to read a QR code (to keep that code from growing too big I'm using super-short keys), gets those keys, and has to translate them to something useful, in my case a protocol.
I'm trying to define a constant called KEY_PROTOCOLS, I think this should be a Map, so later I can call something like KEY_PROTOCOLS.get("f") to get the protocol that belongs to key "f".
Other classes should also be able to import this constant, and use it. So this map has to be populated in the class right away.
How can I do this?
If the constant is shared by several classes, and if you want to make sure this map is not cleared or modified by some code, you'd better make it unmodifiable :
public static final Map<String, String> KEY_PROTOCOLS;
static {
Map<String, String> map = new HashMap<String, String>();
map.put("f", "ftp");
// ...
KEY_PROTOCOLS = Collections.unmodifiableMap(map);
}
Something like this:
private static final Map<String, String> KEY_PROTOCOLS = new HashMap<String, String>();
static{
KEY_PROTOCOLS.put("f", "ftp");
// More
}
Static Initialisers:
http://www.glenmccl.com/tip_003.htm
This would work.
static Map<String, String> map = new HashMap<String, String>();
static {
map.add("ftp", "ftp");
...
}
On android:
#SuppressWarnings("unchecked")
Pair<String,String>[] pre_ips=new Pair[]{new Pair<String,String>("173.194", "0"), new Pair<String,String>("74.125", "96")};
String ip_1_2,ip_3;
for (Pair<String,String> pre_ip:pre_ips)
{ip_1_2=pre_ip.first;
ip_3=pre_ip.second;
}

Categories

Resources