Java 在树结构上创建深度流
因此,我有一个基本的树结构,由树节点组成,这些树节点通过父节点和子节点引用链接到其他树节点。我想创建一个方法,返回一个从叶节点到根节点或从根到叶的流。我已经实现了这一点,但我正在寻找一个解决方案,其中创建的对象数量最少。最好没有。这是我的密码:Java 在树结构上创建深度流,java,tree,java-8,java-stream,Java,Tree,Java 8,Java Stream,因此,我有一个基本的树结构,由树节点组成,这些树节点通过父节点和子节点引用链接到其他树节点。我想创建一个方法,返回一个从叶节点到根节点或从根到叶的流。我已经实现了这一点,但我正在寻找一个解决方案,其中创建的对象数量最少。最好没有。这是我的密码: public class TreeNode<TNode> { private TNode iValue; private TreeNode<TNode> iParentNode = null;
public class TreeNode<TNode> {
private TNode iValue;
private TreeNode<TNode> iParentNode = null;
private List<TreeNode<TNode>> iChildren = new ArrayList<>();
public TreeNode(TNode value) {
this(value, null);
}
private TreeNode(TNode value, TreeNode<TNode> parentNode) {
iValue = value;
iParentNode = parentNode;
}
public Stream<TreeNode<TNode>> streamFromLeaf() {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new LeafFirstIterator(this), Spliterator.SIZED),
false);
}
public Stream<TreeNode<TNode>> streamFromRoot() {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new RootFirstIterator(this), Spliterator.SIZED),
false);
}
public TNode getValue() {
return iValue;
}
public TreeNode<TNode> getParent() {
return iParentNode;
}
public TreeNode<TNode> addChild(TNode childValue) {
TreeNode<TNode> childNode = new TreeNode<TNode>(childValue, iNodeNameFunction, this);
iChildren.add(childNode);
return childNode;
}
public boolean isLeaf() {
return iChildren.size() == 0;
}
public boolean isRoot() {
return iParentNode == null;
}
public List<TreeNode<TNode>> getChildren() {
return iChildren;
}
class LeafFirstIterator implements Iterator<TreeNode<TNode>> {
private TreeNode<TNode> iNextNode;
LeafFirstIterator(TreeNode<TNode> leafNode) {
iNextNode = leafNode;
}
@Override
public boolean hasNext() {
return iNextNode != null;
}
@Override
public TreeNode<TNode> next() {
TreeNode<TNode> current = iNextNode;
iNextNode = current.getParent();
return current;
}
}
class RootFirstIterator implements Iterator<TreeNode<TNode>> {
private List<TreeNode<TNode>> iNodes = new ArrayList<>();
private int iNextIndex;
RootFirstIterator(TreeNode<TNode> leafNode) {
TreeNode<TNode> currentNode = leafNode;
while (currentNode != null) {
iNodes.add(currentNode);
currentNode = currentNode.getParent();
}
iNextIndex = iNodes.size() - 1;
}
@Override
public boolean hasNext() {
return iNextIndex >= 0;
}
@Override
public TreeNode<TNode> next() {
return iNodes.get(iNextIndex--);
}
}
}
公共类树节点{
私人特诺德·伊瓦鲁;
私有树节点iParentNode=null;
private List iChildren=new ArrayList();
公共树节点(TNode值){
该值为(值,null);
}
私有TreeNode(TNode值,TreeNode父节点){
iValue=价值;
iParentNode=父节点;
}
公共流streamFromLeaf(){
返回StreamSupport.stream(Spliterators.spliteratorUnknownSize(新的LeafFirstIterator(this),Spliterator.SIZED),
假);
}
公共流streamFromRoot(){
返回StreamSupport.stream(Spliterators.spliteratorUnknownSize(新的RootFirstIterator(this),Spliterator.SIZED),
假);
}
公共TNode getValue(){
返回伊瓦鲁;
}
公共树节点getParent(){
返回iParentNode;
}
公共树节点addChild(TNode childValue){
TreeNode childNode=新的TreeNode(childValue,iNodeNameFunction,this);
添加(子节点);
返回子节点;
}
公共布尔isLeaf(){
返回iChildren.size()==0;
}
公共布尔值isRoot(){
返回iParentNode==null;
}
公共列表getChildren(){
返回iChildren;
}
类LeafFirstIterator实现了迭代器{
私有树节点;
LeafFirstIterator(TreeNode leafNode){
iNextNode=叶节点;
}
@凌驾
公共布尔hasNext(){
返回inxtnode!=null;
}
@凌驾
公共树节点next(){
三节点电流=不确定节点;
inxtNode=current.getParent();
回流;
}
}
类RootFirstIterator实现了迭代器{
私有列表索引节点=新的ArrayList();
私有不精确索引;
RootFirstIterator(TreeNode leafNode){
TreeNode currentNode=叶节点;
while(currentNode!=null){
添加(当前节点);
currentNode=currentNode.getParent();
}
iNextIndex=iNodes.size()-1;
}
@凌驾
公共布尔hasNext(){
返回不确定性指数>=0;
}
@凌驾
公共树节点next(){
返回iNodes.get(不精确索引--);
}
}
}
这很好,但我的“问题”是,每个流调用都会创建很多对象
- StreamSupport.stream创建新的ReferencePipeline
- Spliterators.spliteratorUnknownSize创建新迭代器Spliterator
- 我创建自己的迭代器实现以传递给拆分器
- RootFirstIterator创建新的ArrayList
public void iterateUp(Consumer<TreeNode<TNode>> consumer) {
doIterateUp(this, consumer);
}
public static <T> void doIterateUp(TreeNode<T> node, Consumer<TreeNode<T>> consumer) {
if (node == null)
return;
consumer.accept(node);
doIterateUp(node.getParent(), consumer);
}
public void iterateUp(消费者){
(这个,消费者);
}
公共静态无效向上(TreeNode节点,使用者){
if(node==null)
返回;
consumer.accept(节点);
doiteraup(node.getParent(),consumer);
}
从根开始向下迭代也同样简单
有什么想法吗?我走错方向了吗?TreeNode应该实现或扩展一些接口/类吗?如果有什么不清楚的地方,请告诉我
谢谢 不要使用流。使用您的备选方案,它有一个名称:the 流并不总是正确的方法,这是使用访问者模式的典型案例
如果您绝对必须拥有流,请让您的使用者收集列表中的节点,然后将其流化,或者将使用者连接到迭代器的
next()
方法(使用一些代码使其正常运行),并使用StreamSupport将其转换为流。但是,我不同意您的性能问题,您的代码有简化的余地,这可能会解决一些您担心的副作用
您犯了一个常见的错误,即从迭代器
实现开始,这很可能是因为迭代器
接口很旧而且很有名。但是实现它很麻烦,如果直接实现Spliterator
,则不需要将迭代器
封装在拆分器
中,这只是一个很好的副作用:
public Stream<TreeNode<TNode>> streamFromLeaf() {
return StreamSupport.stream(new LeafFirstSpliterator<>(this), false);
}
static class LeafFirstSpliterator<TNode>
extends Spliterators.AbstractSpliterator<TreeNode<TNode>> {
private TreeNode<TNode> iNextNode;
LeafFirstSpliterator(TreeNode<TNode> leafNode) {
super(100, ORDERED|NONNULL);
iNextNode = leafNode;
}
public boolean tryAdvance(Consumer<? super TreeNode<TNode>> action) {
if(iNextNode==null) return false;
action.accept(iNextNode);
iNextNode=iNextNode.getParent();
return true;
}
}
这不是必须的,但会非常好。我的另一个想法是将一个过滤器谓词、一个映射函数和一个使用者传递给迭代方法,我将得到类似于func的部分流,并且不会创建任何对象。但我觉得有点难看。将节点收集到列表并进行流式处理,我认为这与我的解决方案的对象创建非常相似,实际上可能更好一些。谢谢!好的指针。正如您所说的,由于迭代器更为常见,所以有一种倾向是使用迭代器。拆分器方法看起来确实更干净。我们将尝试一下,并更仔细地了解新的Java8内容。关于绩效。这段代码对延迟非常关键,为了避免延迟异常值,我们尽量不将GC驱动到太大的程度。
public Stream<TreeNode<TNode>> streamFromRoot() {
ArrayDeque<TreeNode<TNode>> deque = new ArrayDeque<>();
for(TreeNode<TNode> n = this; n != null; n = n.getParent())
deque.addFirst(n);
return deque.stream();
}