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
Related
I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.
I have created the implementation of a abstract method of the super class. Does the code in the method always get executed or is there some kind of cache that knows the code will never change?
I want to know if there are performance issues with my code. Is it better to create the map as a member variable and then return it in the method?
#Override
protected Map<String, Function<Information, String>> getDefinitionMap() {
final Map<String, Function<Information, String>> map = new LinkedHashMap<>();
map.put("Name", t -> t.getName());
map.put("ID", t -> t.getId());
return map;
}
Each time the method getDefinitionMap() is called, a new LinkedHashMap instance is created. There is no "implicit caching".
You can avoid that, if you create the map once, store it in a member variable and return this. You may want to make it unmodifiable so that it cannot be changed by callers. (see java.util.Collections.unmodifiableMap)
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).
When I put a (KEY, VALUE) into a map such as Map<String, List<String>>, and I want to check if the KEY is existed first to decide if I have to make a new List, usually My Java Code looks like this:
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if(example.containsKey(k)){
example.get(k).add(v);
return;
}
List<String> vs = new ArrayList<>();
vs.add(v);
example.put(k,vs);
}
It doesn't looks very nice. Is there any way to make it more simple and more beautiful?
If you have Java 8 you can write this as one line:
example.computeIfAbsent(k, key -> new ArrayList<>()).add(v);
This uses a lambda, so the new ArrayList is only created if required.
(k and key need to have different names, as they are different variables)
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if (!example.containsKey(k)){
example.put(k, new ArrayList<>();
}
example.get(k).add(v);
}
Arguably, this is slightly wasteful - requiring you to get the list you just put - but to my eye it is much cleaner and more expressive.
If you can't use other libraries, or java 8, you could wrap the whole map and construct in a class of you own.
With your own class you:
Confine the messiness to one place.
Hide the face you are using a Map behind the scenes.
Have a place to move any additional logic to.
Is it possible to convert ConcurrentHashMap to HashMap in java ?
This is my sample program where i was converting from ConcurrentHashMap to HashMap but i was getting the following exception:
Exception in thread "main" java.lang.ClassCastException:
java.util.concurrent.ConcurrentHashMap cannot be cast to java.util.HashMap
at com.Hi.main(Hi.java:18)
My code:
package com;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Hi {
public static void main(String args[]) {
Map<String, String> conpage = new ConcurrentHashMap<String, String>();
conpage.put("1", "A");
conpage.put("2", "B");
conpage.put("3", "C");
HashMap hm = (HashMap) conpage;
System.out.println(hm.get("1"));
}
}
Map<String, String> hashMap = new HashMap<String, String>(conpage);
A ConcurrentMap (like ConcurrentHashMap) has no relationship with any AbstractMap, such has HashMap, so the solution is to create a new HashMap adding all values from ConcurrentHashMap during object creation.
Hope this helps.
A ConcurrentHashMap is not a HashMap , so you cannot perform this cast. Treat them as Map regardless of implementation.
Nevertheless , you can use Map#putAll() .
Suggested Reading:
Java - HashMap vs Map objects
What does it mean to “program to an interface”?
Use putAll() method istead of type casting like this:
HashMap hm=new HashMap<String, String>();
hm.putAll(conpage);
ConcurrentHashMap and HashMap are siblings and not Parent-Child related. Hence the cast fails.
To be more explicit in the explanation of the previous comments: consider you have three classes :
class Position {
}
class One extends Position {
String gold = "the best";
}
class Two extends Position {
String silver = "just wait next year!";
}
You cannot do the following cast (note that a cast is not a conversion: it's only a redeclaration of the type)
void showPosition() {
Position one = new One(); // this is regular since One extends Position
Two two = (Two)one; // this is impossible because one is not a two
}
If ever this cast was possible, how would you like the compiler to handle the following promblem? : there is no field called silver in one: so calling
((Two)one).silver
is impossible: returning null would be unsafe, since you wouldn't understand what's happening : since you know that the field 'silver' is initialized to the value "just wait next year!"
Java being a safe language, it doesn't allow this type of errors (actually it's a help from Java that it throws the exception since you think the type is something else that is it is).
The behvior you expect is rather a behavior proper to scripts.
A ConcurrentHashMap is still a Map. So you can create a new TreeMap like this:
ConcurrentHashMap myMap;
...
TreeMap myTreeMap = new TreeMap( myMap );
You can do so in the following way -
HashMap<String, String> map = new HashMap<String, String>(conpage);
JavaDoc.
Typically each concrete type in the Java Collection API (like HashMap, ArrayList etc.) has a constructor which takes a reference of its parent (like Map, List) and constructs a new object from it.
About the exception you are getting, it's because ConcurrentHashMap is not a subtype/supertype of HashMap, thus you are getting a ClassCastException. However, this would have worked fine -
Map<String, String> hm = (Map<String, String> ) conpage;
Actually There is no inheritance relation between HashMap and ConcurrentHashMap that's why while casting the concurrenthashmap object to hashmap its giving the ClassCastException.