why HashMap Values are not cast in List?

JavaArraylistCollectionsHashmap

Java 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 valuesToMatch = new ArrayList(highLowValueMap.values());

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. You could write a code like this to iterate over the set:

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 and you can't cast a Collection into an ArrayList, thus you get ClassCastException."

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 }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbNdView Question on Stackoverflow
Solution 1 - JavaPermGenErrorView Answer on Stackoverflow
Solution 2 - JavacowlsView Answer on Stackoverflow
Solution 3 - JavaAndrew LogvinovView Answer on Stackoverflow
Solution 4 - Javauser195488View Answer on Stackoverflow
Solution 5 - JavaAbimaran KugathasanView Answer on Stackoverflow
Solution 6 - JavaJyothirmayi LalithaView Answer on Stackoverflow
Solution 7 - JavaAxarydaxView Answer on Stackoverflow
Solution 8 - JavaAmitGView Answer on Stackoverflow
Solution 9 - JavaJinView Answer on Stackoverflow
Solution 10 - JavaBraian CoronelView Answer on Stackoverflow