2

Looking for a insertion order collection that also allows efficient querying and subset views of positions (like sublist). Seems the most straightforward option for this would be to take the linked list approach of List, embed the nodes as map values and expose part or all of the list interface on the class.

Would someone bitch to Oracle about this? Having NavigableMap/Set added for sorted maps and sets and not having the far more common insertion order equivalent...

edit: please don't suggest LinkedHashSet - it doesn't have any way to query the position or to do a relative subset.

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
i30817
  • 1,356
  • 2
  • 13
  • 26
  • 2
    Why not implement it yourself...and/or discover that this isn't necessarily a simple data structure to implement. (The constraints you've listed make it pretty difficult to support querying for positions in better than O(n) time...which you can build in with a wrapper around `LinkedHashSet`.) In any event, if you described why you _wanted_ this structure, some libraries might actually be interested in adding this to their APIs. – Louis Wasserman Aug 17 '12 at 00:39
  • The reason is simple: a collection that spends most of it's time iterating and querying for membership on parts of it's elements needs random access, preferably indexed. Thus either a map or a set. The niggle is the 'parts of it'. Where list has a excellent single method for this (sublist, what i consider the most inspired method on the collections framework), only *some* maps/sets allow it on the jdk, and only by implementing the nightmarish Navigable/Set/Map, which is only for sorted collections anyway - when a subSet/Map(index, index) could be made for any Linked variant, adding getIndex – i30817 Aug 17 '12 at 00:54
  • Or if getindex is too expensive (needs to keep a int on the node on some implementations?), just use the object as key directly (for sets-implemented-as-map) and make a 'subset' with the linked list substructure. linkedhashset.subSet(V low, V high). Probably not so easy for Maps though – i30817 Aug 17 '12 at 00:59
  • I mean, you're always welcome to try implementing it yourself, but I still think you'll find it more difficult than you're suggesting. – Louis Wasserman Aug 17 '12 at 01:06
  • I did a simple adaptation of a similar class i'd [done](http://code.google.com/p/bookjar/source/browse/subprojects/bookjar-util/src/i3/util/MRUMap.java) and you're right - for a subMap(K low, K high) the internal state of the Node list is problematic since its pointers can't be carried over without interfering with the original collection. Still if LinkedList manages it it should be possible. – i30817 Aug 17 '12 at 01:47

2 Answers2

2

you mean like java.util.LinkedHashSet:

Hash table and linked list implementation of the Set interface, with predictable iteration order. This implementation differs from HashSet in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is the order in which elements were inserted into the set (insertion-order). Note that insertion order is not affected if an element is re-inserted into the set. (An element e is reinserted into a set s if s.add(e) is invoked when s.contains(e) would return true immediately prior to the invocation.)

Dmitry B.
  • 9,107
  • 3
  • 43
  • 64
  • No i specifically don't mean like linkedhashset, because linkedhashset has no public interface (or private since it extends hashset) to query the position of a object or alternatively creating a subset based on those 'before' or 'after' the object. – i30817 Aug 16 '12 at 23:38
  • 1
    @i30817 So, you mean basically a linked hashset with some additional operations? Here's an ASF licence implementation: http://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/classlib/modules/luni/src/main/java/java/util/LinkedHashSet.java Feel free to enhance it. – Marcin Aug 17 '12 at 01:43
0

edit2: New final version Here is a version only for sets with slightly adjusted function (divided into two, no longer accepts null as 'beginning of the map') that probably has less bugs

public class LinkedSet<E> implements Set<E> {

private LinkedHashMap<E, Integer> m = new LinkedHashMap<E, Integer>();
private int monoticallyIncreasing;

/**
 * Returns true if the value target was added before (exclusive)
 * limitElem in insertion order.
 *
 * If target or limit are not present on the set this method returns false
 *
 * @param limitElem a E that may be a element of the set or not.
 * @return if target was added before limit (can be reset by removing and
 * re-adding the target, that changes iteration order).
 */
public boolean containsBefore(E target, E limitElem) {
    if (isEmpty()) {
        return false;
    }

    Integer targetN = m.get(target);
    if (targetN == null) {
        return false;
    }

    Integer highN = m.get(limitElem);
    if (highN == null) {
        return false;
    }
    return targetN < highN;
}

/**
 * Returns true if the value target was added after (exclusive)
 * previousElem in insertion order.
 *
 * If target or previous are not present on the set this method returns false
 *
 * @param previousElem a E that may be a element of the set or not.
 * @return if target was added before previous (can be reset by removing and
 * re-adding the target, that changes iteration order).
 */
public boolean containsAfter(E target, E previousElem) {
    if (isEmpty()) {
        return false;
    }

    Integer targetN = m.get(target);
    if (targetN == null) {
        return false;
    }

    Integer low = m.get(previousElem);
    if (low == null) {
        return false;
    }
    return low < targetN;
}

@Override
public boolean add(E e) {
    Integer pos = m.get(e);
    if (pos == null) {
        m.put(e, monoticallyIncreasing++);
        return true;
    }
    return false;
}

@Override
public int size() {
    return m.size();
}

@Override
public boolean isEmpty() {
    return m.isEmpty();
}

@Override
public boolean contains(Object o) {
    return m.containsKey(o);
}

@Override
public Object[] toArray() {
    Object[] result = new Object[size()];
    int i = 0;
    for (E e : this) {
        result[i++] = e;
    }
    return result;
}

@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
    int size = size();
    if (a.length < size) {
        a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
    }
    int i = 0;
    Object[] result = a;
    for (E e : this) {
        result[i++] = e;
    }
    if (a.length > size) {
        //peculiar toArray contract where it doesn't care about the rest
        a[size] = null;
    }
    return a;
}

@Override
public boolean remove(Object o) {
    return m.remove(o) != null;
}

@Override
public boolean addAll(Collection<? extends E> c) {
    boolean changed = false;
    for (E e : c) {
        changed |= add(e);
    }
    return changed;
}

@Override
public boolean containsAll(Collection<?> c) {
    return m.keySet().containsAll(c);
}

@Override
public boolean retainAll(Collection<?> c) {
    return m.keySet().retainAll(c);
}

@Override
public boolean removeAll(Collection<?> c) {
    return m.keySet().removeAll(c);
}

@Override
public void clear() {
    m.clear();
}

@Override
public Iterator<E> iterator() {
    return m.keySet().iterator();
}
}
i30817
  • 1,356
  • 2
  • 13
  • 26
  • humm - although it is useful, i'd like a iterator from()/descendingFrom() there too. But LinkedHashMap has no access to their node list or the nodes. guess it's back to the old version. – i30817 Aug 18 '12 at 10:52
  • Shown a implementation of that on this [comment](http://stackoverflow.com/a/12023209/214260) – i30817 Aug 19 '12 at 00:13