6

I'd like to have an object that implements both the Map and the List interfaces in Java. The idea is similar to the problem in this question: Java Ordered Map

I want to add name/value pairs to a list and have the list preserve the sequence, but also be able to do lookups by name:

foo.put("name0", "value0");
foo.put("name1", "value1");
foo.get(1); --> Map.Entry("name1", "value1")
foo.get("name0"); --> "value0"

Here's the problem: when I create this class:

class Foo implements Map, List {
    // add all methods here
}

I get a compile error:

"The return type is incompatible with Map.remove(Object)"
public boolean remove(Object o) {
    return false;
}

If I don't implement the Map and List interfaces, then there are lots of Java collections methods that aren't available to use on this data structure.

(Also, the reason that the solution proposed in Java Ordered Map above doesn't work is that LinkedHashMap doesn't have a get(int) method. Can't select entries by index.)

Community
  • 1
  • 1
ccleve
  • 15,239
  • 27
  • 91
  • 157
  • 2
    Are you against extending the `LinkedHashMap`, if not, why not try adding the `get(int)` method to it? – Mark Elliot Nov 18 '10 at 19:20
  • http://www.java.net/forum/topic/jdk/java-se/implementing-both-map-and-list-impossible-0 – Adam Nov 18 '10 at 19:22
  • 1
    @Adam that link is broken, but I guess we kind of know what it says, just from the URL ;) – Tyler Jun 23 '11 at 21:54
  • If your map is immutable and doesn't have null keys or values, Guava `ImmutableMap` may come to rescue. It's `keySet()` is of type `ImmutableSet` which offers `asList()` view. – Tomáš Záluský Sep 07 '15 at 13:19

6 Answers6

6

It should be pointed out that the reason for the error is that Map contains the definition for the following remove method:

V remove(Object key)

While List defines:

boolean remove(Object o) 

And, in Java, methods cannot be overloaded based on their return type, so they are conflicting signatures, and cannot be implemented in the same class.

jjnguy
  • 136,852
  • 53
  • 295
  • 323
5

LinkedHashMap does what you need.

Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries.

Paulo Guedes
  • 7,189
  • 5
  • 40
  • 60
  • There's no `get` method to get items by index – Adam Nov 18 '10 at 19:24
  • 2
    @Adam: Then extend LinkedHashMap and implement get(int index) using the iterator. You could even use the backing Entry[] array called `table` (though I'd use the iterator for robustness). – extraneon Nov 18 '10 at 19:31
5

As you noticed you cannot implement both List and Map on the same class. But for what you need that should also not be necessary. What you need is that the data can be accessed by both a Map and a List interface. A bit like accessing Map data as a set in entrySet() or as a Collection using Map.values().

In short, what you need is 2 views on the data, one view implementing a List and another view implementing Map.

If there is one view dominant (for example Map) then you could give your map implementation a method List getAsList() which presents the data as a List, backed by the data of the Map.

EDIT

The answer given by Paulo Guedes should serve you. There already is a Map implementation with your requirements. My answer is a bit more general, about presenting the same data using multiple incompatible interfaces where a simple Adapter is not enough.

extraneon
  • 23,575
  • 2
  • 47
  • 51
2

Why don't you implements your own interface?

public interface HashListMap {

public boolean add(Object arg0);
public void add(int arg0, Object arg1);
public boolean addAll(Collection arg0);
public boolean addAll(int arg0, Collection arg1);
public void clear();
public boolean contains(Object arg0); 
public boolean containsAll(Collection arg0);
public Object get(int arg0);
public int indexOf(Object arg0);
public boolean isEmpty();
public Iterator iterator();
public int lastIndexOf(Object arg0);
public ListIterator listIterator();
public ListIterator listIterator(int arg0);
public boolean remove(Object arg0);
public Object remove(int arg0);
public boolean removeAll(Collection arg0);
public boolean retainAll(Collection arg0);
public Object set(int arg0, Object arg1);
public int size();
public List subList(int arg0, int arg1);
public Object[] toArray();
public Object[] toArray(Object[] arg0);
public boolean containsKey(Object arg0);
public boolean containsValue(Object arg0);
public Set entrySet();
public Object get(Object arg0);
public Set keySet();
public Object put(Object arg0, Object arg1);
public void putAll(Map arg0);
public Collection values();

}

SuperJulietta
  • 6,801
  • 1
  • 14
  • 11
1

The Map and List interfaces contain conflicting definitions of a remove method. You can't implement both in a single class because you cannot override the same method signature with a difference in return type only.

I wonder if using List<Map.Entry<K,V>> would fulfill your need.

aioobe
  • 413,195
  • 112
  • 811
  • 826
Dave Costa
  • 47,262
  • 8
  • 56
  • 72
1

In addition to what Dave Costa said you should use LinkedHashMap. This is Map but it preserves order of elements insertion.

As map it implements values() method, so you can say new ArrayList(map.values()).get(0) to mimic list functionality.

but you also can say map.get("one") because it is just a map implementation.

AlexR
  • 114,158
  • 16
  • 130
  • 208