|
|
@@ -1,7 +1,5 @@
|
|
|
package net.ranides.assira.collection.maps;
|
|
|
|
|
|
-import net.ranides.assira.collection.sets.WBTree;
|
|
|
-import net.ranides.assira.collection.sets.WBTreeSet;
|
|
|
import net.ranides.assira.generic.CompareUtils;
|
|
|
|
|
|
import java.io.Serializable;
|
|
|
@@ -11,7 +9,7 @@ import java.util.NoSuchElementException;
|
|
|
|
|
|
public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
- private final WBTree<PEntry<K,V>> tree;
|
|
|
+ private final WBTreeMapBase<K,V> tree;
|
|
|
|
|
|
public WBTreeMap() {
|
|
|
this(CompareUtils.comparator());
|
|
|
@@ -19,9 +17,9 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
public WBTreeMap(Comparator<? super K> comparator) {
|
|
|
super(comparator);
|
|
|
- this.tree = new WBTree<>((Serializable & Comparator<Entry<K,V>>)(a,b) -> {
|
|
|
+ this.tree = new WBTreeMapBase<>((Serializable & Comparator<? super K>)(a,b) -> {
|
|
|
try {
|
|
|
- return comparator.compare(a.getKey(), b.getKey());
|
|
|
+ return comparator.compare(a, b);
|
|
|
} catch (ClassCastException e) {
|
|
|
return -1;
|
|
|
}
|
|
|
@@ -30,20 +28,17 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
@Override
|
|
|
protected Entry<K, V> findEntry(K key) {
|
|
|
- WBTree.Node<PEntry<K, V>> node = tree.find(new PEntry<>(key, null));
|
|
|
- return node != null ? node.value : null;
|
|
|
+ return tree.find(key);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected Entry<K, V> firstEntry() {
|
|
|
- WBTree.Node<PEntry<K, V>> node = tree.first();
|
|
|
- return node != null ? node.value : null;
|
|
|
+ return tree.first();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected Entry<K, V> lastEntry() {
|
|
|
- WBTree.Node<PEntry<K, V>> node = tree.last();
|
|
|
- return node != null ? node.value : null;
|
|
|
+ return tree.last();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -53,7 +48,9 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
@Override
|
|
|
protected ListIterator<Entry<K, V>> entryIterator(K begin) {
|
|
|
- return new TreeIterator(tree.find(new PEntry<>(begin, null), WBTree.FindMode.EQG));
|
|
|
+ WBTreeMapBase.Node<K, V> after = tree.find(begin, WBTreeMapBase.FindMode.GE);
|
|
|
+ int index = WBTreeMapBase.indexOf(after);
|
|
|
+ return new TreeIterator(index < 0 ? tree.size() : index);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -68,8 +65,8 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
@Override
|
|
|
public boolean remove(Object key, Object value) {
|
|
|
- WBTree.Node<PEntry<K, V>> prev = tree.find(new PEntry<>((K) key, (V) value));
|
|
|
- if(prev != null && CompareUtils.equals(prev.value.getValue(), value)) {
|
|
|
+ WBTreeMapBase.Node<K, V> prev = tree.find((K)key);
|
|
|
+ if(prev != null && CompareUtils.equals(prev.value, value)) {
|
|
|
tree.removeNode(prev);
|
|
|
return true;
|
|
|
}
|
|
|
@@ -78,14 +75,19 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
@Override
|
|
|
public V put(K key, V value) {
|
|
|
- WBTree.Node<PEntry<K, V>> prev = tree.add(new PEntry<>(key, value));
|
|
|
- return (prev != null) ? prev.value.getValue() : null;
|
|
|
+ WBTreeMapBase.Node<K, V> prev = tree.find(key);
|
|
|
+ if(prev != null) {
|
|
|
+ return prev.setValue(value);
|
|
|
+ } else {
|
|
|
+ tree.add(key, value);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public V remove(Object key) {
|
|
|
- WBTree.Node<PEntry<K, V>> prev = tree.remove(new PEntry<K, V>((K) key, null));
|
|
|
- return (prev != null) ? prev.value.getValue() : null;
|
|
|
+ WBTreeMapBase.Node<K, V> prev = tree.remove((K) key);
|
|
|
+ return (prev != null) ? prev.value : null;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -99,6 +101,33 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
super(begin, bottom, end, top);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ WBTreeMapBase.Node<K,V> first = $first();
|
|
|
+ WBTreeMapBase.Node<K,V> last = $last();
|
|
|
+ if(first == null || last == null) {
|
|
|
+ return 0;
|
|
|
+ } else {
|
|
|
+ return WBTreeMapBase.indexOf(last) - WBTreeMapBase.indexOf(first) + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private WBTreeMapBase.Node<K,V> $first() {
|
|
|
+ WBTreeMapBase.Node<K,V> out = bottom ? tree.first() : tree.find(begin, WBTreeMapBase.FindMode.GE);
|
|
|
+ if(out == null || !inside(out.key)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
+ public WBTreeMapBase.Node<K,V> $last() {
|
|
|
+ WBTreeMapBase.Node<K,V> out = top ? tree.last() : tree.find(end, WBTreeMapBase.FindMode.LT);
|
|
|
+ if(out == null || !inside(out.key)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
protected ListIterator<Entry<K,V>> entryIterator() {
|
|
|
return new SIterator();
|
|
|
@@ -106,7 +135,20 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
@Override
|
|
|
protected ListIterator<Entry<K,V>> entryIterator(K from) {
|
|
|
- return new SIterator(tree.find(new PEntry<>(WBTreeMap.this.higher(from, begin), null)));
|
|
|
+ WBTreeMapBase.Node<K,V> fromNode;
|
|
|
+ if(bottom) {
|
|
|
+ fromNode = tree.find(from, WBTreeMapBase.FindMode.GT);
|
|
|
+ }
|
|
|
+ else if(compare(from, begin) > 0) {
|
|
|
+ fromNode = tree.find(from, WBTreeMapBase.FindMode.GT);
|
|
|
+ } else {
|
|
|
+ fromNode = tree.find(begin, WBTreeMapBase.FindMode.GE);
|
|
|
+ }
|
|
|
+ if(fromNode == null) {
|
|
|
+ return new SIterator(size());
|
|
|
+ } else {
|
|
|
+ return new SIterator(WBTreeMapBase.indexOf(fromNode) - WBTreeMapBase.indexOf($first()));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -119,169 +161,114 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
@Override
|
|
|
protected Entry<K,V> firstEntry() {
|
|
|
- // If this submap goes end -infinity, we return the main map first entry; otherwise, we locate the start of the map.
|
|
|
- WBTree.Node<PEntry<K, V>> entry;
|
|
|
- if (bottom) {
|
|
|
- entry = tree.first();
|
|
|
- } else {
|
|
|
- entry = tree.find(new PEntry<>(begin, null));
|
|
|
- // If we find either the start or something greater we're OK.
|
|
|
- if (entry!=null && compare(entry.value.getKey(), begin) < 0) {
|
|
|
- entry = WBTree.next(entry);
|
|
|
- }
|
|
|
- }
|
|
|
- // Finally, if this subset doesn't go end infinity, we check that the resulting key isn't greater than the end.
|
|
|
- if (entry == null || !top && compare(entry.value.getKey(), end) >= 0) {
|
|
|
- return null;
|
|
|
+ WBTreeMapBase.Node<K,V> out = $first();
|
|
|
+ if(out == null) {
|
|
|
+ throw new NoSuchElementException();
|
|
|
}
|
|
|
- return entry.value;
|
|
|
+ return out;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- protected Entry lastEntry() {
|
|
|
- if (tree == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- // If this submap goes end infinity, we return the main map last entry; otherwise, we locate the end of the map.
|
|
|
- WBTree.Node<PEntry<K, V>> entry;
|
|
|
- if (top) {
|
|
|
- entry = tree.last();
|
|
|
- } else {
|
|
|
- entry = tree.find(new PEntry<>(begin, null));
|
|
|
- // If we find something smaller than the end we're OK.
|
|
|
- if (entry!=null && compare(entry.value.getKey(), end) >= 0) {
|
|
|
- entry = WBTree.prev(entry);
|
|
|
- }
|
|
|
- }
|
|
|
- // Finally, if this subset doesn't go end -infinity, we check that the resulting key isn't smaller than the start.
|
|
|
- if (entry == null || !bottom && compare(entry.value.getKey(), begin) < 0) {
|
|
|
- return null;
|
|
|
+ protected Entry<K,V> lastEntry() {
|
|
|
+ WBTreeMapBase.Node<K,V> out = $last();
|
|
|
+ if(out == null) {
|
|
|
+ throw new NoSuchElementException();
|
|
|
}
|
|
|
- return entry.value;
|
|
|
+ return out;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
private class SIterator implements ListIterator<Entry<K,V>> {
|
|
|
|
|
|
- private WBTree.Node<PEntry<K,V>> current;
|
|
|
- private WBTree.Node<PEntry<K,V>> prev;
|
|
|
- private WBTree.Node<PEntry<K,V>> next;
|
|
|
- private int offset;
|
|
|
+ private int index;
|
|
|
+ private int last;
|
|
|
+
|
|
|
+ private final int minIndex;
|
|
|
+ private int maxIndex;
|
|
|
+
|
|
|
+ private final int shift;
|
|
|
|
|
|
public SIterator() {
|
|
|
- if(bottom) {
|
|
|
- this.current = tree.first();
|
|
|
- this.next = current;
|
|
|
- this.offset = 0;
|
|
|
+ this(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public SIterator(int index) {
|
|
|
+ this.index = index;
|
|
|
+ this.last = -1;
|
|
|
+ this.shift = index;
|
|
|
+
|
|
|
+ WBTreeMapBase.Node<K,V> first = $first();
|
|
|
+ WBTreeMapBase.Node<K,V> last = $last();
|
|
|
+ if(first == null || last == null) {
|
|
|
+ this.minIndex = 0;
|
|
|
+ this.maxIndex = 0;
|
|
|
} else {
|
|
|
- this.current = tree.find(new PEntry<>(begin,null), WBTree.FindMode.LE);
|
|
|
- this.next = null;
|
|
|
- this.offset = WBTree.indexOf(current);
|
|
|
+ this.minIndex = WBTreeMapBase.indexOf(first);
|
|
|
+ this.maxIndex = WBTreeMapBase.indexOf(last) + 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public SIterator(WBTree.Node<PEntry<K,V>> after) {
|
|
|
- this.current = after;
|
|
|
- this.next = null;
|
|
|
+ @Override
|
|
|
+ public final boolean hasNext() {
|
|
|
+ return minIndex+index < maxIndex;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public boolean hasNext() {
|
|
|
- if(next != null) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- if(current == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- next = WBTree.next(current);
|
|
|
-
|
|
|
- return next != null && inside(next.value.getKey());
|
|
|
+ public final boolean hasPrevious() {
|
|
|
+ return minIndex+index > minIndex;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public Entry<K,V> next() {
|
|
|
- if(next == null) {
|
|
|
- if(current == null) {
|
|
|
- throw new NoSuchElementException();
|
|
|
- }
|
|
|
- next = WBTree.next(current);
|
|
|
- }
|
|
|
-
|
|
|
- prev = current;
|
|
|
- current = next;
|
|
|
- next = null;
|
|
|
-
|
|
|
- if(current == null || !inside(current.value.getKey())) {
|
|
|
+ if (!hasNext()) {
|
|
|
throw new NoSuchElementException();
|
|
|
}
|
|
|
-
|
|
|
- return current.value;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean hasPrevious() {
|
|
|
- if(prev != null) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- if(current == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- prev = WBTree.prev(current);
|
|
|
-
|
|
|
- return prev != null && inside(prev.value.getKey());
|
|
|
+ return tree.at((last = index++) + minIndex);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public Entry<K,V> previous() {
|
|
|
- if(prev == null) {
|
|
|
- if(current == null) {
|
|
|
- throw new NoSuchElementException();
|
|
|
- }
|
|
|
- prev = WBTree.prev(current);
|
|
|
- }
|
|
|
-
|
|
|
- next = current;
|
|
|
- current = prev;
|
|
|
- prev = null;
|
|
|
-
|
|
|
- if(current == null || !inside(current.value.getKey())) {
|
|
|
+ if (!hasPrevious()) {
|
|
|
throw new NoSuchElementException();
|
|
|
}
|
|
|
-
|
|
|
- return current.value;
|
|
|
+ return tree.at((last = --index) + minIndex);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public int nextIndex() {
|
|
|
- // @todo optimize, we should store index
|
|
|
- return offset + WBTree.indexOf(current);
|
|
|
+ public final int nextIndex() {
|
|
|
+ return index - minIndex - shift;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public int previousIndex() {
|
|
|
- // @todo optimize, we should store index
|
|
|
- return offset + WBTree.indexOf(current) - 1;
|
|
|
+ public final int previousIndex() {
|
|
|
+ return index - minIndex - shift - 1;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void remove() {
|
|
|
- if(current == null) {
|
|
|
- throw new IllegalStateException();
|
|
|
- }
|
|
|
- prev = WBTree.prev(current);
|
|
|
- next = WBTree.next(current);
|
|
|
-
|
|
|
- tree.removeNode(current);
|
|
|
- current = null;
|
|
|
+ public final void add(Entry<K,V> value) {
|
|
|
+ throw new UnsupportedOperationException();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void set(Entry<K,V> k) {
|
|
|
+ public final void set(Entry<K,V> value) {
|
|
|
throw new UnsupportedOperationException();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void add(Entry<K,V> k) {
|
|
|
- throw new UnsupportedOperationException();
|
|
|
+ public final void remove() {
|
|
|
+ if (last == -1) {
|
|
|
+ throw new IllegalStateException();
|
|
|
+ }
|
|
|
+ tree.removeNode(tree.at(last + minIndex));
|
|
|
+ // If the last operation was a next(),
|
|
|
+ // we are removing an element *before* us,
|
|
|
+ // and we must decrease pos correspondingly.
|
|
|
+ if (last < index) {
|
|
|
+ index--;
|
|
|
+ }
|
|
|
+ last = -1;
|
|
|
+ maxIndex--;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -289,148 +276,82 @@ public class WBTreeMap<K,V> extends ASortedMap<K,V> {
|
|
|
|
|
|
private class TreeIterator implements ListIterator<Entry<K,V>> {
|
|
|
|
|
|
- private WBTree.Node<PEntry<K,V>> current;
|
|
|
- private WBTree.Node<PEntry<K,V>> prev;
|
|
|
- private WBTree.Node<PEntry<K,V>> next;
|
|
|
+ private int index;
|
|
|
+
|
|
|
+ private int last;
|
|
|
+
|
|
|
+ private final int offset;
|
|
|
|
|
|
public TreeIterator() {
|
|
|
- this.current = tree.first();
|
|
|
- this.next = current;
|
|
|
+ this(0);
|
|
|
}
|
|
|
|
|
|
- public TreeIterator(WBTree.Node<PEntry<K,V>> after) {
|
|
|
- this.current = after;
|
|
|
- this.next = null;
|
|
|
+ public TreeIterator(int index) {
|
|
|
+ this.last = -1;
|
|
|
+ this.index = index;
|
|
|
+ this.offset = index;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public boolean hasNext() {
|
|
|
- if(next != null) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- if(current == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- next = WBTree.next(current);
|
|
|
-
|
|
|
- return next != null;
|
|
|
+ public final boolean hasNext() {
|
|
|
+ return index < size();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public Entry<K,V> next() {
|
|
|
- if(next == null) {
|
|
|
- if(current == null) {
|
|
|
- throw new NoSuchElementException();
|
|
|
- }
|
|
|
- next = WBTree.next(current);
|
|
|
- }
|
|
|
-
|
|
|
- prev = current;
|
|
|
- current = next;
|
|
|
- next = null;
|
|
|
-
|
|
|
- if(current == null) {
|
|
|
- throw new NoSuchElementException();
|
|
|
- }
|
|
|
-
|
|
|
- return current.value;
|
|
|
+ public final boolean hasPrevious() {
|
|
|
+ return index > 0;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public boolean hasPrevious() {
|
|
|
- if(prev != null) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- if(current == null) {
|
|
|
- return false;
|
|
|
+ public final Entry<K,V> next() {
|
|
|
+ if (!hasNext()) {
|
|
|
+ throw new NoSuchElementException();
|
|
|
}
|
|
|
- prev = WBTree.prev(current);
|
|
|
-
|
|
|
- return prev != null;
|
|
|
+ return tree.at(last = index++);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public Entry<K,V> previous() {
|
|
|
- if(prev == null) {
|
|
|
- if(current == null) {
|
|
|
- throw new NoSuchElementException();
|
|
|
- }
|
|
|
- prev = WBTree.prev(current);
|
|
|
- }
|
|
|
-
|
|
|
- next = current;
|
|
|
- current = prev;
|
|
|
- prev = null;
|
|
|
-
|
|
|
- if(current == null) {
|
|
|
+ public final Entry<K,V> previous() {
|
|
|
+ if (!hasPrevious()) {
|
|
|
throw new NoSuchElementException();
|
|
|
}
|
|
|
-
|
|
|
- return current.value;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int nextIndex() {
|
|
|
- // @todo optimize, we should store index
|
|
|
- return WBTree.indexOf(current);
|
|
|
+ return tree.at(last = --index);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public int previousIndex() {
|
|
|
- // @todo optimize, we should store index
|
|
|
- return WBTree.indexOf(current) - 1;
|
|
|
+ public final int nextIndex() {
|
|
|
+ return index - offset;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void remove() {
|
|
|
- if(current == null) {
|
|
|
- throw new IllegalStateException();
|
|
|
- }
|
|
|
- prev = WBTree.prev(current);
|
|
|
- next = WBTree.next(current);
|
|
|
-
|
|
|
- tree.removeNode(current);
|
|
|
- current = null;
|
|
|
+ public final int previousIndex() {
|
|
|
+ return index - offset - 1;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void set(Entry<K,V> k) {
|
|
|
+ public final void add(Entry<K,V> value) {
|
|
|
throw new UnsupportedOperationException();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void add(Entry<K,V> k) {
|
|
|
+ public final void set(Entry<K,V> value) {
|
|
|
throw new UnsupportedOperationException();
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- protected static final class PEntry<K, V> extends AEntry<K, V> implements Serializable {
|
|
|
-
|
|
|
- private static final long serialVersionUID = 2L;
|
|
|
-
|
|
|
- private final K key;
|
|
|
- private V value;
|
|
|
-
|
|
|
- public PEntry(K key, V value) {
|
|
|
- this.key = key;
|
|
|
- this.value = value;
|
|
|
- }
|
|
|
|
|
|
@Override
|
|
|
- public K getKey() {
|
|
|
- return key;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V getValue() {
|
|
|
- return value;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V setValue(V value) {
|
|
|
- V prev = this.value;
|
|
|
- this.value = value;
|
|
|
- return prev;
|
|
|
+ public final void remove() {
|
|
|
+ if (last == -1) {
|
|
|
+ throw new IllegalStateException();
|
|
|
+ }
|
|
|
+ tree.removeNode(tree.at(last));
|
|
|
+ // If the last operation was a next(),
|
|
|
+ // we are removing an element *before* us,
|
|
|
+ // and we must decrease pos correspondingly.
|
|
|
+ if (last < index) {
|
|
|
+ index--;
|
|
|
+ }
|
|
|
+ last = -1;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|