提交 1217a01e authored 作者: Thomas Mueller's avatar Thomas Mueller

A persistent multi-version map (work in progress): r-tree

上级 747b2489
...@@ -9,6 +9,7 @@ package org.h2.test.store; ...@@ -9,6 +9,7 @@ package org.h2.test.store;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.dev.store.btree.BtreeMap; import org.h2.dev.store.btree.BtreeMap;
import org.h2.dev.store.btree.BtreeMapStore; import org.h2.dev.store.btree.BtreeMapStore;
import org.h2.dev.store.btree.Cursor;
import org.h2.dev.store.btree.CursorPos; import org.h2.dev.store.btree.CursorPos;
import org.h2.dev.store.btree.DataType; import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.Page; import org.h2.dev.store.btree.Page;
...@@ -35,7 +36,7 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -35,7 +36,7 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
if (root == null) { if (root == null) {
return null; return null;
} }
return (V) getSpatial(root, key); return (V) get(root, key);
} }
private boolean contains(Page p, int index, Object key) { private boolean contains(Page p, int index, Object key) {
...@@ -53,11 +54,11 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -53,11 +54,11 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
* @param key the key * @param key the key
* @return the value, or null if not found * @return the value, or null if not found
*/ */
protected Object getSpatial(Page p, Object key) { protected Object get(Page p, Object key) {
if (!p.isLeaf()) { if (!p.isLeaf()) {
for (int i = 0; i < p.getKeyCount(); i++) { for (int i = 0; i < p.getKeyCount(); i++) {
if (contains(p, i, key)) { if (contains(p, i, key)) {
Object o = getSpatial(p.getChildPage(i), key); Object o = get(p.getChildPage(i), key);
if (o != null) { if (o != null) {
return o; return o;
} }
...@@ -77,14 +78,14 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -77,14 +78,14 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
if (root == null) { if (root == null) {
return null; return null;
} }
return getPageSpatial(root, key); return getPage(root, key);
} }
protected Page getPageSpatial(Page p, Object key) { protected Page getPage(Page p, Object key) {
if (!p.isLeaf()) { if (!p.isLeaf()) {
for (int i = 0; i < p.getKeyCount(); i++) { for (int i = 0; i < p.getKeyCount(); i++) {
if (contains(p, i, key)) { if (contains(p, i, key)) {
Page x = getPageSpatial(p.getChildPage(i), key); Page x = getPage(p.getChildPage(i), key);
if (x != null) { if (x != null) {
return x; return x;
} }
...@@ -111,7 +112,7 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -111,7 +112,7 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
Page c2 = set(c, writeVersion, key, value); Page c2 = set(c, writeVersion, key, value);
if (c != c2) { if (c != c2) {
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
setChildUpdateBox(p, i, c2); setChildUpdateBox(p, i, c2, key);
break; break;
} }
} }
...@@ -136,10 +137,18 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -136,10 +137,18 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
for (int i = 0; i < p.getKeyCount(); i++) { for (int i = 0; i < p.getKeyCount(); i++) {
if (contains(p, i, key)) { if (contains(p, i, key)) {
Page c = p.getChildPage(i); Page c = p.getChildPage(i);
long oldSize = c.getTotalSize();
Page c2 = removeExisting(c, writeVersion, key); Page c2 = removeExisting(c, writeVersion, key);
if (c != c2) { if (c2 == null) {
// this child was deleted
p.remove(i);
if (p.getKeyCount() == 0) {
removePage(p);
return null;
}
} else if (oldSize != c2.getTotalSize()) {
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
setChildUpdateBox(p, i, c2); setChildUpdateBox(p, i, c2, key);
break; break;
} }
} }
...@@ -147,6 +156,10 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -147,6 +156,10 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
} else { } else {
for (int i = 0; i < p.getKeyCount(); i++) { for (int i = 0; i < p.getKeyCount(); i++) {
if (keyType.equals(p.getKey(i), key)) { if (keyType.equals(p.getKey(i), key)) {
if (p.getKeyCount() == 1) {
removePage(p);
return null;
}
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
p.remove(i); p.remove(i);
break; break;
...@@ -157,21 +170,26 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -157,21 +170,26 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
} }
/** /**
* Set the child and update the bounding box. * Set the child and update the bounding box if required. The bounding box
* is only updated if the key touches or is outside the old bounding box.
* *
* @param p the parent (this page is changed) * @param p the parent (this page is changed)
* @param index the child index * @param index the child index
* @param c the child page * @param c the child page
* @param key the new or old key
*/ */
private void setChildUpdateBox(Page p, int index, Page c) { private void setChildUpdateBox(Page p, int index, Page c, Object key) {
Object oldBounds = p.getKey(index);
if (key == null || !keyType.isInside(key, oldBounds)) {
p.setKey(index, getBounds(c)); p.setKey(index, getBounds(c));
}
p.setChild(index, c); p.setChild(index, c);
} }
private SpatialKey getBounds(Page x) { private Object getBounds(Page x) {
SpatialKey bounds = keyType.copy((SpatialKey) x.getKey(0)); Object bounds = keyType.createBoundingBox(x.getKey(0));
for (int i = 1; i < x.getKeyCount(); i++) { for (int i = 1; i < x.getKeyCount(); i++) {
keyType.increase(bounds, (SpatialKey) x.getKey(i)); keyType.increaseBounds(bounds, x.getKey(i));
} }
return bounds; return bounds;
} }
...@@ -221,15 +239,15 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -221,15 +239,15 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
c = c.copyOnWrite(writeVersion); c = c.copyOnWrite(writeVersion);
Page split = split(c, writeVersion); Page split = split(c, writeVersion);
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
setChildUpdateBox(p, index, c); setChildUpdateBox(p, index, c, null);
p.insert(index, getBounds(split), null, split.getPos(), split.getTotalSize()); p.insert(index, getBounds(split), null, split.getPos(), split.getTotalSize());
// now we are not sure where to add // now we are not sure where to add
return add(p, writeVersion, key, value); return add(p, writeVersion, key, value);
} }
Page c2 = add(c, writeVersion, key, value); Page c2 = add(c, writeVersion, key, value);
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
// the child might be the same, but not the size // the child might be the same, but maybe not the bounding box
setChildUpdateBox(p, index, c2); setChildUpdateBox(p, index, c2, key);
return p; return p;
} }
...@@ -266,8 +284,8 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -266,8 +284,8 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
while (p.getKeyCount() > 0) { while (p.getKeyCount() > 0) {
float diff = 0, bestA = 0, bestB = 0; float diff = 0, bestA = 0, bestB = 0;
int best = 0; int best = 0;
SpatialKey ba = getBounds(newP); Object ba = getBounds(newP);
SpatialKey bb = getBounds(split); Object bb = getBounds(split);
for (int i = 0; i < p.getKeyCount(); i++) { for (int i = 0; i < p.getKeyCount(); i++) {
Object o = p.getKey(i); Object o = p.getKey(i);
float a = keyType.getAreaIncrease(ba, o); float a = keyType.getAreaIncrease(ba, o);
...@@ -301,10 +319,11 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -301,10 +319,11 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
source.remove(sourceIndex); source.remove(sourceIndex);
} }
public void addNodeKeys(ArrayList<SpatialKey> list, Page p) { @SuppressWarnings("unchecked")
public void addNodeKeys(ArrayList<K> list, Page p) {
if (p != null && !p.isLeaf()) { if (p != null && !p.isLeaf()) {
for (int i = 0; i < p.getKeyCount(); i++) { for (int i = 0; i < p.getKeyCount(); i++) {
list.add((SpatialKey) p.getKey(i)); list.add((K) p.getKey(i));
addNodeKeys(list, p.getChildPage(i)); addNodeKeys(list, p.getChildPage(i));
} }
} }
...@@ -317,16 +336,17 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -317,16 +336,17 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
* @param parents the stack of parent page positions * @param parents the stack of parent page positions
* @param key the key * @param key the key
*/ */
protected void min(Page p, ArrayList<CursorPos> parents, Object key) { protected CursorPos min(Page p, Cursor<K, V> cursor, Object key) {
while (p != null) { while (p != null) {
CursorPos c = new CursorPos(); CursorPos c = new CursorPos();
c.page = p; c.page = p;
parents.add(c);
if (p.isLeaf()) { if (p.isLeaf()) {
return; return c;
} }
cursor.push(c);
p = p.getChildPage(0); p = p.getChildPage(0);
} }
return null;
} }
/** /**
...@@ -335,29 +355,25 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> { ...@@ -335,29 +355,25 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
* @param parents the stack of parent page positions * @param parents the stack of parent page positions
* @return the next key * @return the next key
*/ */
protected Object nextKey(ArrayList<CursorPos> parents) { protected Object nextKey(CursorPos p, Cursor<K, V> cursor) {
if (parents.size() == 0) {
return null;
}
while (true) { while (true) {
// TODO performance: avoid remove/add pairs if possible
CursorPos p = parents.remove(parents.size() - 1);
int index = p.index++; int index = p.index++;
if (index < p.page.getKeyCount()) { Page x = p.page;
parents.add(p); if (index < x.getKeyCount()) {
return p.page.getKey(index); return x.getKey(index);
} }
while (true) { while (true) {
if (parents.size() == 0) { p = cursor.pop();
if (p == null) {
return null; return null;
} }
p = parents.remove(parents.size() - 1);
index = ++p.index; index = ++p.index;
if (index < p.page.getKeyCount()) { x = p.page;
parents.add(p); if (index < x.getKeyCount()) {
Page x = p.page; cursor.push(p);
x = x.getChildPage(index); x = x.getChildPage(index);
min(x, parents, null); p = min(x, cursor, null);
cursor.setCurrentPos(p);
break; break;
} }
} }
......
...@@ -6,38 +6,66 @@ ...@@ -6,38 +6,66 @@
*/ */
package org.h2.test.store; package org.h2.test.store;
import java.util.Arrays;
/** /**
* A unique spatial key. * A unique spatial key.
*/ */
public class SpatialKey { public class SpatialKey {
public float[] min;
public float[] max;
public long id; public long id;
private float[] minMax;
/**
* Create a new key.
*
* @param id the id
* @param minMax min x, max x, min y, max y, and so on
*/
public SpatialKey(long id, float... minMax) {
this.id = id;
this.minMax = minMax;
}
public float min(int dim) {
return minMax[dim + dim];
}
public static SpatialKey create(long id, float... minMax) { public void setMin(int dim, float x) {
SpatialKey k = new SpatialKey(); minMax[dim + dim] = x;
k.id = id;
int dimensions = minMax.length / 2;
k.min = new float[dimensions];
k.max = new float[dimensions];
for (int i = 0; i < dimensions; i++) {
k.min[i] = minMax[i + i];
k.max[i] = minMax[i + i + 1];
} }
return k;
public float max(int dim) {
return minMax[dim + dim + 1];
}
public void setMax(int dim, float x) {
minMax[dim + dim + 1] = x;
} }
public String toString() { public String toString() {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.append(id).append(": ("); buff.append(id).append(": (");
for (int i = 0; i < min.length; i++) { for (int i = 0; i < minMax.length; i += 2) {
if (i > 0) { if (i > 0) {
buff.append(", "); buff.append(", ");
} }
buff.append(min[i]).append('/').append(max[i]); buff.append(minMax[i]).append('/').append(minMax[i + 1]);
} }
return buff.append(")").toString(); return buff.append(")").toString();
} }
public int hashCode() {
return (int) ((id >>> 32) ^ id);
}
public boolean equals(Object other) {
if (!(other instanceof SpatialKey)) {
return false;
}
SpatialKey o = (SpatialKey) other;
return Arrays.equals(minMax, o.minMax);
}
} }
...@@ -58,15 +58,15 @@ public class SpatialType implements DataType { ...@@ -58,15 +58,15 @@ public class SpatialType implements DataType {
SpatialKey k = (SpatialKey) obj; SpatialKey k = (SpatialKey) obj;
int flags = 0; int flags = 0;
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
if (k.min[i] == k.max[i]) { if (k.min(i) == k.max(i)) {
flags |= 1 << i; flags |= 1 << i;
} }
} }
DataUtils.writeVarInt(buff, flags); DataUtils.writeVarInt(buff, flags);
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
buff.putFloat(k.min[i]); buff.putFloat(k.min(i));
if ((flags & (1 << i)) == 0) { if ((flags & (1 << i)) == 0) {
buff.putFloat(k.max[i]); buff.putFloat(k.max(i));
} }
} }
DataUtils.writeVarLong(buff, k.id); DataUtils.writeVarLong(buff, k.id);
...@@ -74,20 +74,21 @@ public class SpatialType implements DataType { ...@@ -74,20 +74,21 @@ public class SpatialType implements DataType {
@Override @Override
public Object read(ByteBuffer buff) { public Object read(ByteBuffer buff) {
SpatialKey k = new SpatialKey();
int flags = DataUtils.readVarInt(buff); int flags = DataUtils.readVarInt(buff);
k.min = new float[dimensions]; float[] minMax = new float[dimensions * 2];
k.max = new float[dimensions];
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
k.min[i] = buff.getFloat(); float min = buff.getFloat();
float max;
if ((flags & (1 << i)) != 0) { if ((flags & (1 << i)) != 0) {
k.max[i] = k.min[i]; max = min;
} else { } else {
k.max[i] = buff.getFloat(); max = buff.getFloat();
} }
minMax[i + i] = min;
minMax[i + i + 1] = max;
} }
k.id = DataUtils.readVarLong(buff); long id = DataUtils.readVarLong(buff);
return k; return new SpatialKey(id, minMax);
} }
@Override @Override
...@@ -99,26 +100,19 @@ public class SpatialType implements DataType { ...@@ -99,26 +100,19 @@ public class SpatialType implements DataType {
SpatialKey a = (SpatialKey) objA; SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB; SpatialKey b = (SpatialKey) objB;
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
if (a.max[i] < b.min[i] || a.min[i] > b.max[i]) { if (a.max(i) < b.min(i) || a.min(i) > b.max(i)) {
return false; return false;
} }
} }
return true; return true;
} }
public SpatialKey copy(SpatialKey old) { public void increaseBounds(Object bounds, Object add) {
SpatialKey k = new SpatialKey(); SpatialKey b = (SpatialKey) bounds;
k.min = new float[dimensions]; SpatialKey a = (SpatialKey) add;
k.max = new float[dimensions];
System.arraycopy(old.min, 0, k.min, 0, dimensions);
System.arraycopy(old.max, 0, k.max, 0, dimensions);
return k;
}
public void increase(SpatialKey bounds, SpatialKey add) {
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
bounds.min[i] = Math.min(bounds.min[i], add.min[i]); b.setMin(i, Math.min(b.min(i), a.min(i)));
bounds.max[i] = Math.max(bounds.max[i], add.max[i]); b.setMax(i, Math.max(b.max(i), a.max(i)));
} }
} }
...@@ -134,11 +128,11 @@ public class SpatialType implements DataType { ...@@ -134,11 +128,11 @@ public class SpatialType implements DataType {
SpatialKey b = (SpatialKey) objB; SpatialKey b = (SpatialKey) objB;
float areaOld = 1, areaNew = 1; float areaOld = 1, areaNew = 1;
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
float min = a.min[i]; float min = a.min(i);
float max = a.max[i]; float max = a.max(i);
areaOld *= max - min; areaOld *= max - min;
min = Math.min(min, b.min[i]); min = Math.min(min, b.min(i));
max = Math.max(max, b.max[i]); max = Math.max(max, b.max(i));
areaNew *= max - min; areaNew *= max - min;
} }
return areaNew - areaOld; return areaNew - areaOld;
...@@ -149,8 +143,8 @@ public class SpatialType implements DataType { ...@@ -149,8 +143,8 @@ public class SpatialType implements DataType {
SpatialKey b = (SpatialKey) objB; SpatialKey b = (SpatialKey) objB;
float area = 1; float area = 1;
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
float min = Math.min(a.min[i], b.min[i]); float min = Math.min(a.min(i), b.min(i));
float max = Math.max(a.max[i], b.max[i]); float max = Math.max(a.max(i), b.max(i));
area *= max - min; area *= max - min;
} }
return area; return area;
...@@ -167,11 +161,40 @@ public class SpatialType implements DataType { ...@@ -167,11 +161,40 @@ public class SpatialType implements DataType {
SpatialKey a = (SpatialKey) objA; SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB; SpatialKey b = (SpatialKey) objB;
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
if (a.min[i] > b.min[i] || a.max[i] < b.max[i]) { if (a.min(i) > b.min(i) || a.max(i) < b.max(i)) {
return false; return false;
} }
} }
return true; return true;
} }
/**
* Check whether a given object is completely inside and does not touch the
* given bound.
*
* @param objA the object to check
* @param objB the bounds
* @return true if a is completely inside b
*/
public boolean isInside(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
for (int i = 0; i < dimensions; i++) {
if (a.min(i) <= b.min(i) || a.max(i) >= b.max(i)) {
return false;
}
}
return true;
}
public Object createBoundingBox(Object objA) {
float[] minMax = new float[dimensions * 2];
SpatialKey a = (SpatialKey) objA;
for (int i = 0; i < dimensions; i++) {
minMax[i + i] = a.min(i);
minMax[i + i + 1] = a.max(i);
}
return new SpatialKey(0, minMax);
}
} }
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
package org.h2.dev.store.btree; package org.h2.dev.store.btree;
import java.util.AbstractSet; import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
...@@ -200,7 +199,7 @@ public class BtreeMap<K, V> { ...@@ -200,7 +199,7 @@ public class BtreeMap<K, V> {
* @param parents the stack of parent page positions * @param parents the stack of parent page positions
* @param key the key * @param key the key
*/ */
protected void min(Page p, ArrayList<CursorPos> parents, Object key) { protected CursorPos min(Page p, Cursor<K, V> cursor, Object key) {
while (p != null) { while (p != null) {
if (!p.isLeaf()) { if (!p.isLeaf()) {
int x = key == null ? -1 : p.binarySearch(key); int x = key == null ? -1 : p.binarySearch(key);
...@@ -212,7 +211,7 @@ public class BtreeMap<K, V> { ...@@ -212,7 +211,7 @@ public class BtreeMap<K, V> {
CursorPos c = new CursorPos(); CursorPos c = new CursorPos();
c.page = p; c.page = p;
c.index = x; c.index = x;
parents.add(c); cursor.push(c);
p = p.getChildPage(x); p = p.getChildPage(x);
} else { } else {
int x = key == null ? 0 : p.binarySearch(key); int x = key == null ? 0 : p.binarySearch(key);
...@@ -222,10 +221,10 @@ public class BtreeMap<K, V> { ...@@ -222,10 +221,10 @@ public class BtreeMap<K, V> {
CursorPos c = new CursorPos(); CursorPos c = new CursorPos();
c.page = p; c.page = p;
c.index = x; c.index = x;
parents.add(c); return c;
return;
} }
} }
return null;
} }
/** /**
...@@ -234,29 +233,24 @@ public class BtreeMap<K, V> { ...@@ -234,29 +233,24 @@ public class BtreeMap<K, V> {
* @param parents the stack of parent page positions * @param parents the stack of parent page positions
* @return the next key * @return the next key
*/ */
protected Object nextKey(ArrayList<CursorPos> parents) { protected Object nextKey(CursorPos p, Cursor<K, V> cursor) {
if (parents.size() == 0) {
return null;
}
while (true) { while (true) {
// TODO performance: avoid remove/add pairs if possible
CursorPos p = parents.remove(parents.size() - 1);
int index = p.index++; int index = p.index++;
if (index < p.page.getKeyCount()) { if (index < p.page.getKeyCount()) {
parents.add(p);
return p.page.getKey(index); return p.page.getKey(index);
} }
while (true) { while (true) {
if (parents.size() == 0) { p = cursor.pop();
if (p == null) {
return null; return null;
} }
p = parents.remove(parents.size() - 1);
index = ++p.index; index = ++p.index;
if (index <= p.page.getKeyCount()) { if (index <= p.page.getKeyCount()) {
parents.add(p); cursor.push(p);
Page x = p.page; Page x = p.page;
x = x.getChildPage(index); x = x.getChildPage(index);
min(x, parents, null); p = min(x, cursor, null);
cursor.setCurrentPos(p);
break; break;
} }
} }
......
...@@ -15,17 +15,20 @@ import java.util.Iterator; ...@@ -15,17 +15,20 @@ import java.util.Iterator;
* @param <K> the key type * @param <K> the key type
* @param <V> the value type * @param <V> the value type
*/ */
class Cursor<K, V> implements Iterator<K> { public class Cursor<K, V> implements Iterator<K> {
private final BtreeMap<K, V> map; private final BtreeMap<K, V> map;
private final ArrayList<CursorPos> parents = new ArrayList<CursorPos>(); private final ArrayList<CursorPos> parents = new ArrayList<CursorPos>();
private CursorPos currentPos;
private K current; private K current;
Cursor(BtreeMap<K, V> map, Page root, K from) { Cursor(BtreeMap<K, V> map, Page root, K from) {
this.map = map; this.map = map;
map.min(root, parents, from); currentPos = map.min(root, this, from);
if (currentPos != null) {
fetchNext(); fetchNext();
} }
}
public K next() { public K next() {
K c = current; K c = current;
...@@ -37,7 +40,7 @@ class Cursor<K, V> implements Iterator<K> { ...@@ -37,7 +40,7 @@ class Cursor<K, V> implements Iterator<K> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void fetchNext() { private void fetchNext() {
current = (K) map.nextKey(parents); current = (K) map.nextKey(currentPos, this);
} }
public boolean hasNext() { public boolean hasNext() {
...@@ -48,5 +51,18 @@ class Cursor<K, V> implements Iterator<K> { ...@@ -48,5 +51,18 @@ class Cursor<K, V> implements Iterator<K> {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public void setCurrentPos(CursorPos p) {
currentPos = p;
}
public void push(CursorPos p) {
parents.add(p);
}
public CursorPos pop() {
int size = parents.size();
return size == 0 ? null : parents.remove(size - 1);
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论