Roland
Roland

Reputation: 7853

Generic Map with Lists of Comparable

I want a Map with several lists, each of them composed of Comparable objects, but not always the same, so there might be a List of Doubles and a List of Strings, etc...

Map<String, List<Comparable<?>>>

Unfortunately the Map as defined above will not be useful because the elements in each List can't be compared. Therefore I had to cheat and introduce a type parameter to the class:

class MyClass<T extends Comparable<? super T>> {
    Map<String, List<T>> myMap;
}

This is not totally correct because not all lists are of the same type. But it works, the only thing I have to do is to do a type cast to T when I add a Double to a list of doubles or a String to a list of strings. I make sure that each type gets added only to lists of the correct type.

Is there a better way to solve this problem?

The alternative is to have one Map for every type of List. But this just would make the code uglier and the only thing I do with the lists is to sort them and insert them into a DB as String values, therefore I only call toString on the comparables.

Upvotes: 3

Views: 1782

Answers (3)

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147154

This is looking a bit like a heterogeneous map. What are you actually going to do with your list of comparables other than compare them?

However, if you want the lists to be of different types, then the indirection should be around the List.

public final class SortableList<T extends Comparable<? super T>> {
    private final List<T> elements;
    public SortableList(List<T> elements) {
        this.elements = elements;
    }
    public List<T> elements() {
        return elements;
    }
}

...

Map<String,SortableList<?>> sortables;
...
Comparable<?> colaMax = Collections.max(sortables.get("shirley").elements());

Upvotes: 0

shizhz
shizhz

Reputation: 12501

From the docs of java generic wildcard it says:

In generic code, the question mark (?), called the wildcard, represents an unknown type.

And also:

The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.

So if you have the following declaration:

List<?> list = new ArrayList<>();

Then you can't add anything to list, because you just told compiler that "I don't know the type of objects which are going to be put in list"

So your code List<Comparable<?>> means that "I want a list to contains comparable objects, but I don't know its type", which is not OK for compiler, because the compiler do need to know the type info for its work.

To use list contains any Comparable objects, you can use upper bounded wildcards like:

Map<String, List<? extends Comparable>> map = new HashMap<>();

Then you're fine to put any comparable list as value for this map:

public static void main(String[] args) {
    Map<String, List<? extends Comparable>> map = new HashMap<>();
    List<Integer> as = new ArrayList<>();
    as.add(1);
    as.add(5);
    as.add(3);

    map.put("as", as);

    List<String> bs = new ArrayList<>();
    bs.add("hello");
    bs.add("world");
    bs.add("generics");

    map.put("bs", bs);
}

Finally I guess you'll be interested in inheritance and subtypes in generics, hope this could be helpful to you.

Upvotes: 0

Devendra Lattu
Devendra Lattu

Reputation: 2802

You can use List<Object> to make it generic and this purpose.
You might want to do instance of checks for some specific datatypes inside list.

Map<String, List<Object>> map = new HashMap<>();

List<Object> list;

list = new ArrayList<>(Arrays.asList(new Object[] { 1, 2, 3.45, "dev", 'A', true, false }));
map.put("key1", list);

list = new ArrayList<>(Arrays.asList(new Object[] { false, 100, 5.1234f, "user", 'Z', true }));
map.put("key2", list);

for (Map.Entry<String, List<Object>> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " => " + entry.getValue());
}

Output:
key1 => [1, 2, 3.45, dev, A, true, false] key2 => [false, 100, 5.1234, user, Z, true]

Upvotes: 1

Related Questions