Warum werden HashMap-Werte nicht in List umgewandelt?

77

Ich setze Werte in die Hashmap ein, die die Form hat.

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

Ich möchte eine Liste mit der values()Methode der Karte erstellen .

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

oder

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

Es wird jedoch eine Ausnahme ausgelöst:

Ausnahme im Thread "main" java.lang.ClassCastException:
java.util.HashMap $ Werte können nicht in java.util.List umgewandelt werden

Aber es erlaubt mir, es an die Erstellung einer Liste weiterzugeben:

List<Double> valuesToMatch  = new ArrayList<Double>( highLowValueMap.values());
bNd
quelle
5
Die valuesMethode gibt eine Sammlung zurück, keine Liste.
Wahrnehmung

Antworten:

135

TL; DR

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

Erläuterung

Weil a HashMap#values()zurückgibt java.util.Collection<V>und man a nicht Collectionin ein werfen kann ArrayList, bekommt manClassCastException .

Ich würde vorschlagen, ArrayList(Collection<? extends V>)Konstruktor zu verwenden. Dieser Konstruktor akzeptiert ein Objekt, das Collection<? extends V>als Argument implementiert wird . Sie werden nicht bekommen, ClassCastExceptionwenn Sie das Ergebnis HashMap.values()wie folgt bestehen :

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

Gehen Sie weiter in den Java API-Quellcode ein

HashMap # values ​​(): Überprüfen Sie den Rückgabetyp in der Quelle und fragen Sie sich, ob ein java.util.CollectionCasting möglich ist java.util.ArrayList. Nein

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

ArrayList (Collection): Überprüfen Sie den Argumenttyp in der Quelle. Kann eine Methode, deren Argument ein Supertyp ist, einen Untertyp akzeptieren? Ja

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);
}
PermGenError
quelle
22

Die Antwort finden Sie im JavaDoc

Die values()Methode gibt a zurückCollection

Damit

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

Sollte sein

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

Sie können diese Sammlung weiterhin wie eine Liste durchlaufen.

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


Das funktioniert:

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

Weil ArrayListhat einen Konstruktor, der eine Sammlung akzeptiert.

Hauben
quelle
5

Dies liegt daran, dass values()Rückgaben, Collectiondie laut Quellcode HashMapvom Typ sind AbstractCollectionund daher nicht umgewandelt werden können List.

Sie können das ArrayListÜbergeben des values()Ergebnisses instanziieren , da der ArrayListKonstruktor dies Collectionals Argument verwenden kann.

Andrew Logvinov
quelle
3

Wenn Sie bereits eine Instanz Ihres List-Subtyps erstellt haben (z. B. ArrayList, LinkedList), können Sie die addAll-Methode verwenden.

z.B,

valuesToMatch.addAll(myCollection)

Viele Listensubtypen können auch die Quellensammlung in ihren Konstruktor übernehmen.


quelle
2

Haben Sie die API überprüft, was von der values()Methode zurückgegeben wird? Und welcher ArrayList- Konstruktor akzeptiert?

Abimaran Kugathasan
quelle
2

Ich hatte das gleiche Problem, aber dann wurde mir klar, dass die Werte () Collection zurückgeben und keine Liste. Wir können jedoch eine neue ArrayList wie folgt instanziieren:

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

Weil ArrayList einen Konstruktor hat, der Collection als Argument verwenden kann.

Jyothirmayi Lalitha
quelle
1

Das liegt daran, dass Ihre Werte wirklich ein HashSet sind. Sie könnten einen Code wie diesen schreiben, um über die Menge zu iterieren:

List<Double> valuesToMatch=new ArrayList<>();
for(Double d : highLowValueMap.values(){
  valuesToMatch.put(d);
}
Axarydax
quelle
Nein, die values()Methode gibt a Collectionund a keySet()zurück Set. docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
Everton
Sie fügen der ArrrayList Elemente hinzu, indem Sie hinzufügen, nicht setzen, was für Set gilt.
WebComer
1

Valuesist eine innere Klasse in der HashMapKlasse (siehe $ symbol in java.util.HashMap$Values).
Die HashMap.values ​​() -Methode gibt Valuesdas Objekt der Klasse zurück, das keine ListSchnittstelle implementiert . So ist das ClassCastException.
Hier ist die Valuesinnere private Klasse in HashMap, die keine ListSchnittstelle implementiert . Auch AbstractCollection implementiert keine ListSchnittstelle.
AbstractCollectionimplementiert CollectionSchnittstelle. Also nicht in der Lage zu besetzen 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();
        }
    }

Aktualisieren

Das Folgende ist einer der Konstruktoren 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);
    }

Der hashmapObj.values()Methodenrückgabetyp ist also Collection. Welche Klasse implementiert nun diese Collection-Schnittstelle? Die Antwort ist eine ValuesKlasse, die sich innerhalb der HashMap-Klasse (innere Klasse) befindet. Der zurückgegebene Wert von hashmapObj.values()kann an den obigen ArrayList-Konstruktor übergeben werden, der gültig ist.

Auch das Folgende ist gültige Aussagen.

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

Die folgenden Aussagen sind jedoch falsch

HashMap<String, String> map  = new HashMap<String, String>();
List c = map.values(); //compilation error.. return type is not List
AmitG
quelle
Ihre Antwort ist am verständlichsten. Ich konnte nicht verstehen, warum ich das kann Collection<String> c = new ArrayList<>();, konnte aber nicht values()auf diesen Typ oder sogar auf diesen Typ umsteigen List.
Ashur
@ ashur Dies liegt daran, dass Sie auf diese Weise Code an die Schnittstelle senden, was akzeptabel ist. Sie können Collection in Ihrem Beispiel in List ändern (da es sich auch um eine Schnittstelle handelt), und es ist immer noch in Ordnung, aber Sie können nicht in Schnittstelle umwandeln.
WebComer
1

Ich bezweifle die ausgewählte beste Antwort, in der es heißt: "Da HashMap # values ​​() eine java.util.Collection zurückgibt und Sie eine Sammlung nicht in eine ArrayList umwandeln können, erhalten Sie ClassCastException."

Dies liegt nicht daran, dass Collection nicht in ArrayList umgewandelt werden kann, sondern daran, dass die von HashMap.values ​​() zurückgegebene Collection von der inneren Klasse HashMap.Values ​​gesichert wird. Und HashMap.Values ​​ist keine Superklasse von ArrayList.

Jin
quelle