why we can change a hashMap which is declared as blank final, but we cannot change a primitive type?
for example
if I create a map
final Map<String, String> someMap;
and initialize it in constructor, and still I can put values in this. But same is not the case with primitive
final int a;
I cant change the value of a in this case. can somebody explain this ?
final means it cannot be changed once initialized. You are just declaring the variable but not initializing it, hence it is allowed.
So doing this is valid
final Map<String, String> someMap;
someMap = new HashMap<String, String>();
But if you try to assign another value to it post initialization then compiler should throw an error that final variable is already intialized:
final Map<String, String> someMap;
someMap = new HashMap<String, String>();
someMap = new TreeMap<String, String>(); //error here
Note: Also putting/removing values in hashmap does not change the reference of the final variable.
It is just the reference to the map (i.e. the variable someMap) which cannot be changed. The map itself can be changed. You can for example insert values. But you cannot assign a new map to someMap.
When using the final keyword on variables you are saying that the variable can be defined only once. In other words once a value has been assigned to the variable, it cannot be reassigned.
This yields obvious behavior with primitive types but is less obvious with objects. Importantly though when inserting values into a map, the object instance remains the same. This is important to remember when passing objects to methods, and really important when using get/set/clone methods as you may end up with multiple references to the same object, where a change in one place (insert entry into map) may have undefined effects in others.
If the Map in your question is important you can use java.util.Collections.unmodifiableMap(m); to stop people fiddling with it.
*emphasized text*When you write:
final Map<String, String> someMap;
it's important to realise that someMap is a reference, and you're declaring the reference to be final. The actual object is not immutable, but the reference is. Hence you can't change the reference i.e. you can't do:
someMap = anotherMap;
later on.
Related
Considering this piece of code, in which I have two maps, a Map<String, Integer> and a Map<String, String>. I assign the second one to an Object and cast this object to a Map so that I can putAll this map to the first one.
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
Map<String, String> otherMap = new HashMap<>();
otherMap.put("two", "two");
Object obj = otherMap;
map.putAll((Map<String,Integer>)obj);
Assert.assertFalse(map.get("two") instanceof Integer);
Assert.assertEquals("{one=1, two=two}", map.toString());
The first assert ensures that the second element of my Map is not a Integer, so how come the putAll did not fail ?
The second assert is jus here to show that there is no apparent problem to this map.
How can I make sure that the putAll method will fail, when the map is first assigned to an Object ?
Thanks
Generics are a compile-time feature, and are not enforced at runtime. Your code would compile with unchecked warnings telling you exactly this: that your code might behave unexpectedly.
To massively oversimplify, all Maps are treated as a Map<Object, Object> at runtime. (Not really, but sort of.)
You could, if you really wanted, use Collections.checkedMap to wrap the map and enforce the type-safety at runtime. There's an associated performance cost though.
I use this common initialization format when I anticipate changing the implementation of the List interface at a later time:
List<Foo> foos = new ArrayList<Foos>();
In an effort to gain the same utility for the values within a Map, I attempted the following but my compiler whines about List<> and ArrayList<> being incompatible types.
Map<String, List<Foo>> fooMap = new HashMap<String, ArrayList<Foo>>;
I've been unable to find an explanation for why I cannot initialize the map in this manner and I'd like to understand the reasoning.
And, sure, this works...
Map<String, List<Foo>> foosMap = new HashMap<String, List<Foo>>;
// ... populate map
ArrayList<Foo> foosAryLst = (ArrayList)foosMap.get("key1");
... but I'm a curious castaphobe. I'd rather fix compile-time errors than runtime errors, things like this aggravate my OCD and the smell of casting conjures an odor similar to the urinal trough after free deep-fried asparagus night at the stadium.
My questions come down to:
Why can I not code my map values to an interface.
Is there a workaround that doesn't require casting?
Any input will be appreciated, thanks!
Sure, there's a workaround that doesn't require casting: don't cast; write
List<Foo> foosLst = foosMap.get("key1");
...and code to the interface with the List as well as the Map.
The root issue, though, is that a Map<String, ArrayList<Foo>> isn't substitutable wherever you'd use Map<String, List<Foo>>. In particular,
Map<String, List<Foo>> map = new HashMap<>();
map.put("foo", new LinkedList<Foo>());
works, but not if map is a Map<String, ArrayList<Foo>>. So one isn't a drop-in substitute for the other.
The declaration that you proposed
Map<String, List<Foos>> fooMap = new HashMap<String, ArrayList<Foos>>();
simply does not make sense: The variable fooMap has the type Map<String, List<Foos>>. This means:
every value that you obtain from this map is a List<Foos>
you may put every value into this list that is (of a subtype of) List<Foos>
If you wanted a map that has ArrayLists as its values, then you would declare it as
Map<String, ArrayList<Foos>> fooMap = new HashMap<String, ArrayList<Foos>>();
If you don't care about the list type, then you can say
Map<String, List<Foos>> fooMap = new HashMap<String, List<Foos>>();
But there's no sensible meaning of mixing the two. Even if you could write what you proposed, then you could still not obtain an ArrayList from this map, because this is simply not the type that fooMap was declared with.
In most cases,
Map<String, List<Foos>> fooMap = new HashMap<String, List<Foos>>();
should be appropriate. Depending on the use case, one could possibly go further by saying
Map<String, List<? extends Foos>> fooMap = new HashMap<String, List<? extends Foos>>();
This way, you can also put lists into the map that contain sublcasses of Foos, like
List<SpecialFoos> specialFoos = ...
fooMap.put("special", specialFoos);
But of course, it's up to you to decide whether this is necessary or not.
The core of the problem is that the compiler cannot keep track of what fooMap may have been assigned to at any particular point in the execution of your code, so there is no way for the compiler to know that
fooMap.put("abc", new ArrayList<Foo>())
should be legal, but that
fooMap.put("abc", new LinkedList<Foo>())
should not be.
All that the compiler knows about the typing of fooMap is its declared type Map<String, List<Foo>>. So, it enforces that whatever object to which you assign fooMap must be able to support all of the operations which a generic Map<String, List<Foo>> is capable of executing. The second line of code above is clearly legal for a Map<String, List<Foo>>, but not legal for a Map<String, ArrayList<Foo>>, so the compiler forbids you from assigning fooMap to a Map<String, ArrayList<Foo>>.
What I would like to do is have a map that actually holds values as one thing, but is declared as another e.g. actually hold the value as String, but put/get will use Integer...
Map<String,String> map = new HashMap<String,String>();
I can use this map with
map.put("A","1");
String ret = map.get("A");
but this will get me a String, and I need to 'put' in a String too.
What I would like is for the put/get methods to accept an Integer value (but the map still stores ...
map.put("A",1);
Integer ret = map.get("A");
How can I achieve this?
N.B. this isn't exclusively for String/Integer conversion, but just conversion between any types.
Thanks.
You can use Object as the value type. It can store String, Integer, Double, for that matter almost anything. But you need to be very careful when using Object because you'll have to cast each value you get from the map accordingly(else you'll always get a ClassCastException).
Map<String, Object> map = new HashMap<String, Object>();
FYI, I do not recommended you to use this. Instead be sure what your Map has to hold and have the value type accordingly.
Simply
Map<String,Integer> map = new HashMap<String,Integer>();
You can (but not SHOULD use, really) this critter: https://gist.github.com/eltabo/8953176. Really... it's evil.
Only for educational purpose.
Why should someone want to store a value in a different representation than the value is made out of? You still can create a string out of an integer and reverse after obtaining it from the map, but what is your advantage?
Within my java application I have a hashmap that holds a string and an Integer. I'm in a situation where I need to return an object where the key is a certain value. I'm not sure how I would go about doing this. Your support would be greatly appreciated.
public HashMap<String, Integer> loginArenaList = new HashMap();
You need Map#get(Object) method:
loginArenaList.get(key);
BTW, your declaration of map is wrong. You are missing generics type on RHS. And of course, you should declare the reference as private, unless you have strong reasons to use public. Should be:
private Map<String, Integer> loginArenaList = new HashMap<>(); // In Java 7
According to the declared map, your keys are of type String and the object to be retrieved is of type Integer. Assuming that you have the key in the variable "key", all you have to do is use the get method.
loginArenaList.get(key);
I want to carry a HashMap over as a static member for each instance of a new class. Every time I try to .get or.put into my HashMap, however, I get a NullPointerException. Help!?
I'm doing: public class EmailAccount {
private static HashMap<String,Integer> name_list; and then name_list.put(last_name, occurences); Even name_list.containsKey(last_name); returns NullPointer.
This comes from an earlier question: Count occurrences of strings in Java
You need to instantiate it.
private static Map<String, Integer> name_list = new HashMap<String, Integer>();
See also:
Java tutorial - Creating objects
Java collections tutorial - The Map interface
Note that using "list" in variable name of a map is confusing. Don't you want it to be a name_map or name_occurences? That underscore does by the way also not really fit in Java naming conventions, but that aside.
You still need to initialize it, like
private static HashMap<String, Integer> name_list = new HashMap<String, Integer>();
When you leave a class-level object field with no initialization -- or any object reference, for that matter, it defaults to null.
While it may seem obvious to you that you want a HashMap, so it should just implicitly initialize it, Java doesn't know if you want in fact a HashMap, or maybe a HashMap subclass, like LinkedHashMap
Class-level primitives, like int can be left just like private static int someNumber; and won't throw a NullPointerException by accessing it--but that's because primitives can't be null. Java will assign it some default value (in int's case, 0).
You didn't instantiate the list. You declared it, but didn't instantiate.
You created a field that can hold a HashMap, but you didn't put anything inside of it
You need to put a new HashMap<String, Integer>() into your field.