Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,38 @@ public int size() {
}
}

// ------------- Helper method for put() -------------
@Override
public Iterator<KeyValuePair<O>> iterator() {
return new LazyIterator<KeyValuePair<O>>() {

final LinkedList<NodeKeyPair> stack = new LinkedList<NodeKeyPair>();
{
stack.push(new NodeKeyPair(root, root.getIncomingEdge()));
}

@Override
protected KeyValuePair<O> computeNext() {
while (!stack.isEmpty()) {
NodeKeyPair current = stack.removeFirst();
List<Node> childNodes = current.node.getOutgoingEdges();
// -> Iterate child nodes in reverse order and so push them onto the stack in reverse order,
// to counteract that pushing them onto the stack alone would otherwise reverse their processing order.
// This ensures that we actually process nodes in ascending alphabetical order.
for (int i = childNodes.size(); i > 0; i--) {
Node child = childNodes.get(i - 1);
stack.addFirst(new NodeKeyPair(child, CharSequences.concatenate(current.key, child.getIncomingEdge())));
}
Object value = current.node.getValue();
if (value != null) {
return new KeyValuePairImpl<O>(CharSequences.toString(current.key), value);
}
}
return endOfData();
}
};
}

// ------------- Helper method for put() -------------

/**
* Atomically adds the given value to the tree, creating a node for the value as necessary. If the value is already
Expand Down Expand Up @@ -811,7 +842,7 @@ protected NodeKeyPair computeNext() {


/**
* Encapsulates a node and its associated key. Used internally by {@link #lazyTraverseDescendants}.
* Encapsulates a node and its associated key. Used internally by {@link #lazyTraverseDescendants} and {@link #iterator()}.
*/
protected static class NodeKeyPair {
public final Node node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*
* @author Niall Gallagher
*/
public interface RadixTree<O> {
public interface RadixTree<O> extends Iterable<KeyValuePair<O>> {

/**
* Associates the given value with the given key; replacing any previous value associated with the key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,10 +457,13 @@ public int size() {
return radixTree.size();
}

@Override
public Iterator<KeyValuePair<O>> iterator() {
return radixTree.iterator();
}

@Override
public Node getNode() {
return radixTree.getNode();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@

import com.googlecode.concurrenttrees.common.CharSequences;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.common.LazyIterator;
import com.googlecode.concurrenttrees.radix.ConcurrentRadixTree;
import com.googlecode.concurrenttrees.radix.node.Node;
import com.googlecode.concurrenttrees.radix.node.NodeFactory;
import com.googlecode.concurrenttrees.radix.node.util.PrettyPrintable;

import java.io.Serializable;
import java.util.Iterator;

/**
* An implementation of {@link ReversedRadixTree} which supports lock-free concurrent reads, and allows items to be added
Expand Down Expand Up @@ -124,6 +126,26 @@ public int size() {
return radixTree.size();
}

@Override
public Iterator<KeyValuePair<O>> iterator() {
return new LazyIterator<KeyValuePair<O>>() {

final Iterator<KeyValuePair<O>> it = radixTree.iterator();

@Override
protected KeyValuePair<O> computeNext() {
if (it.hasNext()) {
KeyValuePair<O> current = it.next();
return new ConcurrentRadixTree.KeyValuePairImpl<O>(
CharSequences.toString(CharSequences.reverse(current.getKey())),
current.getValue());
} else {
return endOfData();
}
}
};
}

@Override
public Node getNode() {
return radixTree.getNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* @see com.googlecode.concurrenttrees.radix.RadixTree
* @author Niall Gallagher
*/
public interface ReversedRadixTree<O> {
public interface ReversedRadixTree<O> extends Iterable<KeyValuePair<O>> {

/**
* Associates the given value with the given key; replacing any previous value associated with the key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,42 +371,16 @@ protected O computeNext() {
*/
@Override
public Iterable<KeyValuePair<O>> getKeyValuePairsForKeysContaining(final CharSequence fragment) {
final Iterable<Set<String>> values = radixTree.getValuesForKeysStartingWith(fragment);
return new Iterable<KeyValuePair<O>>() {
@Override
public Iterator<KeyValuePair<O>> iterator() {
return new LazyIterator<KeyValuePair<O>>() {

Iterator<Set<String>> originalKeysSets = radixTree.getValuesForKeysStartingWith(fragment).iterator();
Iterator<String> keyIterator = Collections.<String>emptyList().iterator();

// A given fragment can be contained many times within the same key, so track keys processed
// so far, so that we can avoid re-processing the same key multiple times...
Set<String> keysAlreadyProcessed = new HashSet<String>();

return ConcurrentSuffixTree.this.iterator(values, new KeyIterator<Set<String>>() {
@Override
protected KeyValuePair<O> computeNext() {
String originalKey = null;
O value = null;
while (value == null) {
while (!keyIterator.hasNext()) {
if (!originalKeysSets.hasNext()) {
return endOfData();
}
keyIterator = originalKeysSets.next().iterator();
}
originalKey = keyIterator.next();

if (keysAlreadyProcessed.add(originalKey)) {
// Key was not in the already-processed set, so proceed with looking up the value...
value = valueMap.get(originalKey);

// value could still be null due to race condition if key/value was removed while
// iterating, hence if so, we loop again to find the next non-null key/value...
}
}
return new ConcurrentRadixTree.KeyValuePairImpl<O>(originalKey, value);
public Iterable<String> toKeys(Set<String> value) {
return value;
}
};
});
}
};
}
Expand All @@ -419,6 +393,58 @@ public int size() {
return valueMap.size();
}

@Override
public Iterator<KeyValuePair<O>> iterator() {
return iterator(radixTree, new KeyIterator<KeyValuePair<Set<String>>>() {
@Override
public Iterable<String> toKeys(KeyValuePair<Set<String>> value) {
return value.getValue();
}
});
}

<V> Iterator<KeyValuePair<O>> iterator(final Iterable<V> it, final KeyIterator<V> resolver) {
return new LazyIterator<KeyValuePair<O>>() {

final Iterator<V> originalKeysSets = it.iterator();
Iterator<String> keyIterator = Collections.<String>emptyList().iterator();

// A given fragment can be contained many times within the same key, so track keys processed
// so far, so that we can avoid re-processing the same key multiple times...
final Set<String> keysAlreadyProcessed = new HashSet<String>();

@Override
protected KeyValuePair<O> computeNext() {
String originalKey = null;
O value = null;
while (value == null) {
while (!keyIterator.hasNext()) {
if (!originalKeysSets.hasNext()) {
return endOfData();
}
V current = originalKeysSets.next();
keyIterator = resolver.toKeys(current).iterator();
}
originalKey = keyIterator.next();

if (keysAlreadyProcessed.add(originalKey)) {
// Key was not in the already-processed set, so proceed with looking up the value...
value = valueMap.get(originalKey);

// value could still be null due to race condition if key/value was removed while
// iterating, hence if so, we loop again to find the next non-null key/value...
}
}
return new ConcurrentRadixTree.KeyValuePairImpl<O>(originalKey, value);
}
};
}

private interface KeyIterator<V> {

Iterable<String> toKeys(V value);
}

/**
* Utility method to return an iterator for the given iterable, or an empty iterator if the iterable is null.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*
* @author Niall Gallagher
*/
public interface SuffixTree<O> {
public interface SuffixTree<O> extends Iterable<KeyValuePair<O>> {

/**
* Associates the given value with the given key; replacing any previous value associated with the key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.io.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import static org.junit.Assert.*;
Expand Down Expand Up @@ -963,6 +964,29 @@ public void testSearchResult_FailureToClassify3() {
new ConcurrentRadixTree.SearchResult("DUMMY", dummyNodeFound, 4, 70, null, null);
}

@Test
public void testIteration() {
ConcurrentRadixTree<Integer> tree = new ConcurrentRadixTree<Integer>(getNodeFactory());
tree.put("TEST", 1);
tree.put("TEAM", 2);
tree.put("TOAST", 3);

Iterator<KeyValuePair<Integer>> it = tree.iterator();
assertTrue(it.hasNext());
KeyValuePair<Integer> team = it.next();
assertEquals("TEAM", team.getKey());
assertEquals(2, (Object) team.getValue());
assertTrue(it.hasNext());
KeyValuePair<Integer> test = it.next();
assertEquals("TEST", test.getKey());
assertEquals(1, (Object) test.getValue());
assertTrue(it.hasNext());
KeyValuePair<Integer> toast = it.next();
assertEquals("TOAST", toast.getKey());
assertEquals(3, (Object) toast.getValue());
assertFalse(it.hasNext());
}

@Test
public void testSerialization() {
ConcurrentRadixTree<Integer> tree1 = new ConcurrentRadixTree<Integer>(getNodeFactory());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
package com.googlecode.concurrenttrees.radixinverted;

import com.googlecode.concurrenttrees.common.Iterables;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.common.PrettyPrinter;
import com.googlecode.concurrenttrees.radix.node.NodeFactory;
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
import com.googlecode.concurrenttrees.testutil.TestUtility;
import org.junit.Test;

import java.util.Iterator;

import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;

Expand Down Expand Up @@ -440,6 +443,29 @@ public void testInheritedMethods() {
assertEquals("[(FOO, 2), (FOOD, 1)]", Iterables.toString(tree.getKeyValuePairsForClosestKeys("FOB")));
}

@Test
public void testIteration() {
ConcurrentInvertedRadixTree<Integer> tree = new ConcurrentInvertedRadixTree<Integer>(getNodeFactory());
tree.put("TEST", 1);
tree.put("TEAM", 2);
tree.put("TOAST", 3);

Iterator<KeyValuePair<Integer>> it = tree.iterator();
assertTrue(it.hasNext());
KeyValuePair<Integer> team = it.next();
assertEquals("TEAM", team.getKey());
assertEquals(2, (Object) team.getValue());
assertTrue(it.hasNext());
KeyValuePair<Integer> test = it.next();
assertEquals("TEST", test.getKey());
assertEquals(1, (Object) test.getValue());
assertTrue(it.hasNext());
KeyValuePair<Integer> toast = it.next();
assertEquals("TOAST", toast.getKey());
assertEquals(3, (Object) toast.getValue());
assertFalse(it.hasNext());
}

@Test
public void testSerialization() {
ConcurrentInvertedRadixTree<Integer> tree1 = new ConcurrentInvertedRadixTree<Integer>(getNodeFactory());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
package com.googlecode.concurrenttrees.radixreversed;

import com.googlecode.concurrenttrees.common.Iterables;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.common.PrettyPrinter;
import com.googlecode.concurrenttrees.radix.node.NodeFactory;
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
import com.googlecode.concurrenttrees.testutil.TestUtility;
import org.junit.Test;

import java.util.Iterator;

import static org.junit.Assert.*;

/**
Expand Down Expand Up @@ -167,6 +170,29 @@ public void testRemove() throws Exception {
assertEquals(expected, actual);
}

@Test
public void testIteration() {
ConcurrentReversedRadixTree<Integer> tree = new ConcurrentReversedRadixTree<Integer>(getNodeFactory());
tree.put("TEST", 1);
tree.put("TEAM", 2);
tree.put("TOAST", 3);

Iterator<KeyValuePair<Integer>> it = tree.iterator();
assertTrue(it.hasNext());
KeyValuePair<Integer> team = it.next();
assertEquals("TEAM", team.getKey());
assertEquals(2, (Object) team.getValue());
assertTrue(it.hasNext());
KeyValuePair<Integer> toast = it.next();
assertEquals("TOAST", toast.getKey());
assertEquals(3, (Object) toast.getValue());
assertTrue(it.hasNext());
KeyValuePair<Integer> test = it.next();
assertEquals("TEST", test.getKey());
assertEquals(1, (Object) test.getValue());
assertFalse(it.hasNext());
}

@Test
public void testSerialization() {
ConcurrentReversedRadixTree<Integer> tree1 = new ConcurrentReversedRadixTree<Integer>(getNodeFactory());
Expand Down
Loading