简体   繁体   中英

Java dynamic object storage in HashSet

i need to store ambiguous Objects like this (each has its own class):

ObjectA 
ObjectB
ObjectC

The HashSet created looks like:

public static HashSet<Object> objects = new HashSet<Object>();

then i place the objects in one at a time:

object.add(new ObjectA());

However here's the problem, if later on i need to retrieve certain objects like this by iterating:

for(Object obj : objects){
    if(obj instanceof ObjectA){
        // print that the object is A
    }
}

Nothing ever happens, and even if i know the only object in there is ObjectA i get this exception:

Caused by: java.lang.ClassCastException: java.util.HashSet$Node cannot be cast to ***.*****.******.objects.ObjectA

What is causing this? Why are the Objects all of a sudden not an instance of themselves?

This problem has nothing to do with generics. This problem as to do with Java not knowing what kind of Object you want when you are doing for (Object obj : objects) { ... } .

Explanation

I believe you typed the error message wrong; I think it should be this (emphasis added by me):

Caused by: java.lang.ClassCastException: java.util.Hash Map $Node cannot be cast to ***.*****.******.objects.ObjectA

The reason for the error is that a Java HashSet is backed by a Java HashMap with null for values. Internally a HashMap uses the HashMap$Node class to store it's elements:

// from the Java source code for HashMap
/**
 * Basic hash bin node, used for most entries.  (See below for
 * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
 */
// Inner class of HashMap - inner classes are always named like OuterClass$InnerClass in bytecode
static class Node<K,V> implements Map.Entry<K,V> { ... }

Remember that because all classes in Java eventually derive from Object , so when you iterate a HashSet like this...

for (Object obj : objects){
    if(obj instanceof ObjectA){
        // print that the object is A
    }
}

... Object obj could be literally anything . Java apparently finds the first match to an Object that it can find and decides that the internal HashMap$Node class that stores elements would be a suitable match for an Object (because it is). It then proceeds to iterate over and give you HashMap$Node 's instead of the actual values that you want. That is probably the cause of your ClassCastException - you got HashMap$Node instead of the Objects you wanted.

Solution

Since all objects derive from, well Object , the only way to tell Java that you want actual values would be using the old-fashioned Iterator<E> method before the days of the enhanced- for (this will go in place of your current for loop):

Iterator<Object> iter = objects.iterator();
Object obj;
while (iter.hasNext()) {
    obj = iter.next();
    if (obj instanceof ObjectA) {
        // ... do stuff ...
    }
}

This should work like you want because it is guaranteed to give you only your values because it is implemented like this in HashSet :

// from the Java source code for HashSet
public Iterator<E> iterator() {
    return map.keySet().iterator(); // only get the keys from the underlying HashMap, i.e. the values.
}

We'll never know what the OP wrote, but here's how you could plausibly end up getting the error message reported while doing something similar to what was described in the question. (The OP has confirmed that the error message referred to HashMap$Node, not HashSet$Node). It's all supposition. If you like this, upvote dudeprgm's answer, not mine .

public class Main {

    static class ObjectA {}

    static class ObjectB {}

    static class ObjectC {}

    public static void main(String[] args) {
        Map<Object, Integer> map = new HashMap<>();
        map.put(new ObjectA(), 1);
        map.put(new ObjectB(), 2);
        map.put(new ObjectC(), 3);
        // objects will contain ObjectA, ObjectB and ObjectC instances. 
        Set<Object> objects = new HashSet<Object>();
        objects.add(new ObjectA());
        objects.add(new ObjectB());
        objects.addAll(map.entrySet()); // Here we mean to do map.keySet().
        for (Object obj : objects) {
            if (obj instanceof ObjectB) {
                ObjectB objB = (ObjectB) obj;
                // Do something with objB
            } else if (obj instanceof ObjectC) {
                ObjectC objC = (ObjectC) obj;
                // Do something with objC
            } else {
                ObjectA objA = (ObjectA) obj;
                // Do something with objA
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM