inet.ipaddr.format.util.TreeOps Maven / Gradle / Ivy
Show all versions of ipaddress Show documentation
/*
* Copyright 2020 Sean C Foley
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* or at
* https://github.com/seancfoley/IPAddress/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inet.ipaddr.format.util;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import inet.ipaddr.format.util.BinaryTreeNode.CachingIterator;
/**
* TreeOps is an interface to the operations supported by both trees and tree nodes: traversals, cloning, and serialization.
*
* The traversal orders are demonstrated by the following code:
*
static class Node extends BinaryTreeNode<Integer> {
private static final long serialVersionUID = 1L;
Node(int i) {
super(i);
setAdded(true);
}
protected void setUpper(int upper) {
super.setUpper(new Node(upper));
}
protected void setLower(int lower) {
super.setLower(new Node(lower));
}
@Override
public Node getUpperSubNode() {
return (Node) super.getUpperSubNode();
}
@Override
public Node getLowerSubNode() {
return (Node) super.getLowerSubNode();
}
}
static void trieOrders() {
Node root = new Node(1);
root.setLower(2);
root.setUpper(3);
root.getLowerSubNode().setLower(4);
root.getLowerSubNode().setUpper(5);
root.getUpperSubNode().setLower(6);
root.getUpperSubNode().setUpper(7);
root.getLowerSubNode().getLowerSubNode().setLower(8);
root.getLowerSubNode().getLowerSubNode().setUpper(9);
root.getLowerSubNode().getUpperSubNode().setLower(10);
root.getLowerSubNode().getUpperSubNode().setUpper(11);
root.getUpperSubNode().getLowerSubNode().setLower(12);
root.getUpperSubNode().getLowerSubNode().setUpper(13);
root.getUpperSubNode().getUpperSubNode().setLower(14);
root.getUpperSubNode().getUpperSubNode().setUpper(15);
PrintStream out = System.out;
out.println(root.toTreeString(true, false));
out.println("natural tree order:");
print(root.nodeIterator(true));
out.println("reverse natural tree order:");
print(root.nodeIterator(false));
out.println("pre-order traversal, lower node first:");
print(root.containingFirstIterator(true));
out.println("pre-order traversal, upper node first:");
print(root.containingFirstIterator(false));
out.println("post-order traversal, lower node first:");
print(root.containedFirstIterator(true));
out.println("post-order traversal, upper node first:");
print(root.containedFirstIterator(false));
}
static void print(Iterator<? extends BinaryTreeNode<Integer>> iterator) {
PrintStream out = System.out;
while(iterator.hasNext()) {
Integer i = iterator.next().getKey();
out.print(i);
out.print(' ');
}
out.println();
out.println();
}
The code gives the following output (the tree is printed with lower nodes before upper nodes):
● 1
├─● 2
│ ├─● 4
│ │ ├─● 8
│ │ └─● 9
│ └─● 5
│ ├─● 10
│ └─● 11
└─● 3
├─● 6
│ ├─● 12
│ └─● 13
└─● 7
├─● 14
└─● 15
natural tree order:
8 4 9 2 10 5 11 1 12 6 13 3 14 7 15
reverse natural tree order:
15 7 14 3 13 6 12 1 11 5 10 2 9 4 8
pre-order traversal, lower node first:
1 2 4 8 9 5 10 11 3 6 12 13 7 14 15
pre-order traversal, upper node first:
1 3 7 15 14 6 13 12 2 5 11 10 4 9 8
post-order traversal, lower node first:
8 9 4 10 11 5 2 12 13 6 14 15 7 3 1
post-order traversal, upper node first:
15 14 7 13 12 6 3 11 10 5 9 8 4 2 1
* @author scfoley
*
* @param
*/
public interface TreeOps extends Iterable, Serializable, Cloneable {
/**
* Traverses the added node keys in natural tree order.
*
* See {@link TreeOps} for more details on the ordering.
*
* @return
*/
@Override
Iterator iterator();
/**
* Traverses the added node keys in reverse natural tree order.
*
* See {@link TreeOps} for more details on the ordering.
* @return
*/
Iterator descendingIterator();
/**
* Creates a {@link java.util.Spliterator} over the keys of the added nodes in natural tree order.
*
* See {@link TreeOps} for more details on the ordering.
*
* @return
*/
@Override
default Spliterator spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
/**
* Creates a {@link java.util.Spliterator} over the keys of the added nodes in descending natural tree order.
*
* See {@link TreeOps} for more details on the ordering.
*
* @return
*/
default Spliterator descendingSpliterator() {
return Spliterators.spliteratorUnknownSize(descendingIterator(), 0);
}
/**
* Iterates through the added nodes in forward or reverse natural tree order.
*
* This iterator supports the {@link java.util.Iterator#remove()} operation.
*
* See {@link TreeOps} for more details on the ordering.
*
*
* @param forward if true, goes in ascending order, otherwise descending
* @return
*/
Iterator extends BinaryTreeNode> nodeIterator(boolean forward);
/**
* Iterates through the nodes (not just the added nodes) in forward or reverse tree order.
*
*
* See {@link TreeOps} for more details on the ordering.
* This iterator supports the {@link java.util.Iterator#remove()} operation.
*
*
* @param forward if true, goes in ascending order, otherwise descending
* @return
*/
Iterator extends BinaryTreeNode> allNodeIterator(boolean forward);
/**
* Returns an iterator that does a pre-order binary tree traversal of the added nodes.
* All added nodes will be visited before their added sub-nodes.
* For an address trie this means added containing subnet blocks will be visited before their added contained addresses and subnet blocks.
*
* This iterator supports the {@link java.util.Iterator#remove()} operation.
*
* Once a given node is visited, the iterator allows you to cache an object corresponding to the
* lower or upper sub-node that can be retrieved when you later visit that sub-node.
*
* Objects are cached only with nodes to be visited.
* So for this iterator that means an object will be cached with the first added lower or upper sub-node,
* the next lower or upper sub-node to be visited,
* which is not necessarily the direct lower or upper sub-node of a given node.
*
* The caching allows you to provide iteration context from a parent to its sub-nodes when iterating.
* The caching and retrieval is done in constant-time and linear space (proportional to tree size).
*
* See {@link TreeOps} for more details on the ordering.
*
* @param forwardSubNodeOrder if true, a left sub-node will be visited before the right sub-node of the same parent node.
* @return
*/
CachingIterator extends BinaryTreeNode, E, C> containingFirstIterator(boolean forwardSubNodeOrder);
/**
* Returns an iterator that does a pre-order binary tree traversal.
* All nodes will be visited before their sub-nodes.
* For an address trie this means containing subnet blocks will be visited before their contained addresses and subnet blocks.
*
* This iterator supports the {@link java.util.Iterator#remove()} operation.
*
* Once a given node is visited, the iterator allows you to cache an object corresponding to the
* lower or upper sub-node that can be retrieved when you later visit that sub-node.
* That allows you to provide iteration context from a parent to its sub-nodes when iterating.
* The caching and retrieval is done in constant-time and linear space (proportional to tree size).
*
* Here is an example showing usage of the caching. Consider this recursive code doing a pre-order traversal:
*
IPv6AddressTrie ipv6Tree = ...;
visitRecursive(ipv6Tree.getRoot(), null);
static <E> void visitRecursive(BinaryTreeNode<E> node, String direction) {
if(direction == null) {
direction = "root";
}
System.out.println("visited " + direction + " " + node);
BinaryTreeNode<E> sub = node.getLowerSubNode();
if(sub != null) {
visitRecursive(sub, direction + " left");
}
sub = node.getUpperSubNode();
if(sub != null) {
visitRecursive(sub, direction + " right");
}
}
* The following iterative code provides the same functionality:
visitIterative(ipv6Tree.getRoot());
static <E> void visitIterative(BinaryTreeNode<E> node) {
CachingIterator<? extends BinaryTreeNode<E>, E, String>iterator = node.containingFirstAllNodeIterator(true);
while(iterator.hasNext()) {
BinaryTreeNode<E> next = iterator.next();
String direction = iterator.getCached();
if(direction == null) {
direction = "root";
}
System.out.println("visited " + direction + " " + next);
iterator.cacheWithLowerSubNode(direction + " left");
iterator.cacheWithUpperSubNode(direction + " right");
}
}
*
*
* See {@link TreeOps} for more details on the ordering.
*
* @param forwardSubNodeOrder if true, a left sub-node will be visited before the right sub-node of the same parent node.
* @return
*/
CachingIterator extends BinaryTreeNode, E, C> containingFirstAllNodeIterator(boolean forwardSubNodeOrder);
/**
* Returns an iterator that does a post-order binary tree traversal of the added nodes.
* All added sub-nodes will be visited before their parent nodes.
* For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks.
*
* This iterator supports the {@link java.util.Iterator#remove()} operation.
*
* See {@link TreeOps} for more details on the ordering.
*
* @param forwardSubNodeOrder if true, a left sub-node will be visited before the right sub-node of the same parent node.
* @return
*/
Iterator extends BinaryTreeNode> containedFirstIterator(boolean forwardSubNodeOrder);
/**
* Returns an iterator that does a post-order binary tree traversal.
* All sub-nodes will be visited before their parent nodes.
* For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks.
*
* This iterator does not support the {@link java.util.Iterator#remove()} operation.
* If {@link java.util.Iterator#remove()} is called it will throw {@link UnsupportedOperationException}.
*
* See {@link TreeOps} for more details on the ordering.
*
* @param forwardSubNodeOrder if true, a left sub-node will be visited before the right sub-node of the same parent node.
* @return
*/
Iterator extends BinaryTreeNode> containedFirstAllNodeIterator(boolean forwardSubNodeOrder);
/**
* Creates a {@link java.util.Spliterator} over the added nodes in forward or reverse natural tree order.
*
* See {@link TreeOps} for more details on the ordering.
*
* @param forward if true, goes in ascending order, otherwise descending
* @return
*/
default Spliterator extends BinaryTreeNode> nodeSpliterator(boolean forward) {
return Spliterators.spliteratorUnknownSize(nodeIterator(forward), 0);
}
/**
* Creates a {@link java.util.Spliterator} over the nodes in forward or reverse natural tree order.
*
* See {@link TreeOps} for more details on the ordering.
*
* @param forward if true, goes in ascending order, otherwise descending
* @return
*/
default Spliterator extends BinaryTreeNode> allNodeSpliterator(boolean forward) {
return Spliterators.spliteratorUnknownSize(allNodeIterator(forward), 0);
}
}