why HashMap Values are not cast in List?
JavaArraylistCollectionsHashmapJava Problem Overview
I'm putting values into the hashmap which is of the form,
Map<Long, Double> highLowValueMap=new HashMap<Long, Double>();
highLowValueMap.put(1l, 10.0);
highLowValueMap.put(2l, 20.0);
I want to create a list by using values()
method of map.
List<Double> valuesToMatch=new ArrayList<>();
valuesToMatch=(List<Double>) highLowValueMap.values();
or
List<Double> valuesToMatch=(List<Double>) highLowValueMap.values();
However, it throws an exception:
> Exception in thread "main" java.lang.ClassCastException:
> java.util.HashMap$Values cannot be cast to java.util.List
But it allows me to pass it in to the creation of a list:
List<Double> valuesToMatch = new ArrayList<Double>( highLowValueMap.values());
Java Solutions
Solution 1 - Java
TL;DR
List<V> al = new ArrayList<V>(hashMapVar.values());
Explanation
Because HashMap#values()
returns a java.util.Collection<V>
and you can't cast a Collection
into an ArrayList
, thus you get ClassCastException
.
I'd suggest using ArrayList(Collection<? extends V>)
constructor. This constructor accepts an object which implements Collection<? extends V>
as an argument. You won't get ClassCastException
when you pass the result of HashMap.values()
like this:
List<V> al = new ArrayList<V>(hashMapVar.values());
Going further into the Java API source code
HashMap#values(): Check the return type in the source, and ask yourself, can a java.util.Collection
be casted into java.util.ArrayList
? No
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
ArrayList(Collection): Check the argument type in the source. Can a method which argument is a super type accepts sub type? Yes
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
Solution 2 - Java
The answer can be found by reading the JavaDoc
The values()
method returns a Collection
So
List<Double> valuesToMatch=(List<Double>) highLowValueMap.values();
Should be
Collection<Double> valuesToMatch= highLowValueMap.values();
You can still iterate over this collection as you would a list.
http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html#values%28%29
This works:
List<Double> valuesToMatch = new ArrayList<Double>( highLowValueMap.values() );
Because ArrayList
has a constructor that accepts a collection.
Solution 3 - Java
It's because values()
returns Collection
which according to source code of HashMap
is of type AbstractCollection
and thus cannot be cast to List
.
You are able to instantiate ArrayList
passing it values()
result because ArrayList
constructor can take Collection
as its argument.
Solution 4 - Java
If you have already created an instance of your List subtype (e.g., ArrayList, LinkedList), you could use the addAll method.
e.g.,
valuesToMatch.addAll(myCollection)
Many list subtypes can also take the source collection in their constructor.
Solution 5 - Java
Have you check the API, what is returned by values()
method? And what ArrayList constructor accepts?
Solution 6 - Java
I faced the same issue, But then I realised the values() return Collection, and not a List. But we are able to instantiate a new ArrayList like this :
List
Because ArrayList has a constructor that can take Collection as its argument.
Solution 7 - Java
Well it's because your values are really a HashSet
List<Double> valuesToMatch=new ArrayList<>();
for(Double d : highLowValueMap.values(){
valuesToMatch.put(d);
}
Solution 8 - Java
Values
is an inner class in HashMap
class (see $ symbol in java.util.HashMap$Values
).
HashMap.values() method will return Values
class's object which is not implementing List
interface. So is the ClassCastException
.
Here is the Values
inner private class in HashMap which is not implementing List
interface. Even AbstractCollection is also not implementing List
interface.
AbstractCollection
implements Collection
interface. So not able to cast to List
.
private final class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
Update
Following is one of the constructor in ArrayList.
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
So hashmapObj.values()
method return type is Collection
. Now which class is implementing this Collection interface ? Answer is Values
class which is inside the HashMap class (inner class). Returned value from hashmapObj.values()
can be passed to above ArrayList constructor which is valid.
Even following is valid statements.
HashMap<String, String> map = new HashMap<String, String>();
Collection c = map.values();
But following statements are incorrect
HashMap<String, String> map = new HashMap<String, String>();
List c = map.values(); //compilation error.. return type is not List
Solution 9 - Java
I doubt the selected best answer, where it says:
"Because HashMap#values() returns a java.util.Collection
It's not because Collection can't be casted to ArrayList, the real reason is that the Collection returned by HashMap.values() is backed up by the inner class HashMap.Values. And HashMap.Values is not a super class of ArrayList.
Solution 10 - Java
To convert the values from a Map instance to a list you could use Iterable<T>.map
val yourList: List<Any> = #Map.values.map { it }