为什么HashMap的值不能转换为List?

91

我正在向哈希映射表中插入值,其形式为:

Map<Long, Double> highLowValueMap=new HashMap<Long, Double>();
highLowValueMap.put(1l, 10.0);
highLowValueMap.put(2l, 20.0);

我想使用映射的 values() 方法创建一个列表。

List<Double> valuesToMatch=new ArrayList<>();
valuesToMatch=(List<Double>) highLowValueMap.values();
或者
List<Double> valuesToMatch=(List<Double>) highLowValueMap.values();

然而,它抛出了一个异常:

Exception in thread "main" java.lang.ClassCastException:
java.util.HashMap$Values cannot be cast to java.util.List

但是它允许我将其传递给列表的创建:

List<Double> valuesToMatch  = new ArrayList<Double>( highLowValueMap.values());

5
values 方法返回的是一个集合(collection),而非列表(list)。 - Perception
10个回答

159

简而言之

List<V> al = new ArrayList<V>(hashMapVar.values());

解释

因为HashMap#values()返回的是一个java.util.Collection<V>,你无法将Collection强制转换为ArrayList,因此你会得到ClassCastException

我建议使用ArrayList(Collection<? extends V>)构造函数。这个构造函数接受实现了Collection<? extends V>接口的对象作为参数。当您像这样传递HashMap.values()的结果时,您不会得到ClassCastException

List<V> al = new ArrayList<V>(hashMapVar.values());

深入Java API源代码

HashMap#values(): 检查源代码中的返回类型,并问自己,一个java.util.Collection能否被强制转换为java.util.ArrayList?不行。

public Collection<V> values() {
    Collection<V> vs = values;
    return (vs != null ? vs : (values = new Values()));
}

ArrayList(Collection): 检查源代码中的参数类型。一个以超类作为参数的方法是否可以接受子类类型?是的。

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

27

你可以通过阅读JavaDoc来找到答案。

values()方法返回一个Collection

因此,

List<Double> valuesToMatch=(List<Double>) highLowValueMap.values();

应该是

Collection<Double> valuesToMatch= highLowValueMap.values();
你仍然可以像遍历列表一样遍历这个集合。

http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html#values%28%29


这个可以运行:

List<Double> valuesToMatch  = new ArrayList<Double>( highLowValueMap.values() );

因为 ArrayList 有一个接受集合类型的构造函数。


6
这是因为 values() 返回的是 Collection,而根据 HashMap 的源代码,它是类型为 AbstractCollection 的,因此无法将其强制转换为 List
你可以使用 ArrayList 实例化它,并将 values() 结果作为参数传递给它,因为 ArrayList 构造函数可以接受 Collection 作为参数。

5
如果您已经创建了列表子类型的实例(例如ArrayList,LinkedList),则可以使用addAll方法。
例如:
valuesToMatch.addAll(myCollection)

许多列表子类型也可以在其构造函数中使用源集合。

2

你是否检查了API,values()方法返回了什么?以及ArrayList 构造函数接受什么参数?


2

我曾经遇到过同样的问题,但是后来我意识到values()返回的是集合而不是列表。 但是我们可以像这样实例化一个新的ArrayList:

List valuesToMatch = new ArrayList(highLowValueMap.values());

因为ArrayList有一个可以接受Collection作为参数的构造函数。


1

这是因为您的值实际上是一个HashSet。您可以编写以下代码来迭代集合:

List<Double> valuesToMatch=new ArrayList<>();
for(Double d : highLowValueMap.values(){
  valuesToMatch.put(d);
}

不对,values() 方法返回一个 Collection,而 keySet() 返回一个 Set。http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html - everton
你应该使用 add 方法向 ArrayList 中添加元素,而不是 put 方法,put 方法适用于 Set。 - WebComer

1
ValuesHashMap类中的一个内部类(在java.util.HashMap$Values中可以看到$符号)。
HashMap.values()方法将返回Values类的对象,该对象没有实现List接口。因此会出现ClassCastException
以下是HashMap中的Values内部私有类,它也没有实现List接口。即使AbstractCollection也没有实现List接口。
AbstractCollection实现了Collection接口。因此无法强制转换为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();
        }
    }

更新

以下是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);
    }

因此,hashmapObj.values()方法的返回类型是Collection。现在哪个类实现了这个Collection接口?答案是HashMap类内部的Values类(内部类)。从hashmapObj.values()返回的值可以传递到上面的ArrayList构造函数中,这是有效的。

以下语句也是有效的。

HashMap<String, String> map  = new HashMap<String, String>();
Collection c = map.values();

但以下陈述是不正确的。
HashMap<String, String> map  = new HashMap<String, String>();
List c = map.values(); //compilation error.. return type is not List

你的回答是最易懂的。我之前不明白为什么能够使用Collection<String> c = new ArrayList<>(); 但是无法将 values()转换成那个类型或者甚至是 List - ashur
@ ashur 这是因为这样你可以编写接口,这是可以接受的。在你的示例中,你可以将 Collection 更改为 List(因为它也是一个接口),这仍然是可以的,但你不能强制转换为接口。 - WebComer

1
我对所选的最佳答案表示怀疑,其中说到: “由于HashMap#values()返回一个java.util.Collection,您无法将Collection转换为ArrayList,因此会出现ClassCastException。”
并不是因为Collection不能转换为ArrayList,真正的原因是HashMap.values()返回的Collection由内部类HashMap.Values支持。而HashMap.Values不是ArrayList的超类。

-1
要将Map实例中的值转换为列表,您可以使用Iterable<T>.map
val yourList: List<Any> = #Map.values.map { it }

这不是Java。 - Luciano Brum
通过JVM的互操作性可以提供服务。 - Braian Coronel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接