10

我认为这Iterator.copy()将是一个非常方便的功能。您可以以更好的方式实现迭代器过滤器。

例如,Googles Java Collection 中使用filter(和类似的)函数的唯一原因UnmodifiableIterator(这只是一个Iteratorwithout remove)是因为您无法实现这样的过滤器,Iterator否则无法在某些时候复制它。(真的,当前界面无法做到这一点;请自己尝试。)

另一个优点是您可以在 for-each-loop 中使用迭代器:因为可复制的迭代器也将自动成为可迭代的。另请参阅问题。现在,不允许这样做的主要设计原因是因为Iterator它实现了迭代器Iterable并且Iterator<T> iterator() { return this; }会使迭代器无效。通过拥有一个copy函数,它很简单,Iterator<T> iterator() { return copy(); }并且不会使原始迭代器无效。因此,没有理由再不允许这样做了。

有什么理由吗?只是为了降低实现它的复杂性?

4

11 回答 11

10

虽然它们通常是,但理论上迭代器不必链接到集合。例如,输入流上的复制方法将难以实现,并且很容易导致模糊的内存问题。

于 2010-09-30T15:13:28.650 回答
5

AnIterator表示来自源的Iterable中的位置(在 java 中),并且不能保证可以复制甚至访问流的源。

例如,您可以在从网络服务器流式传输字节时对其进行迭代,在这种情况下,不可能告诉网络服务器在流中途“从这个位置开始,我希望你向我发送两次相同的字节,但是在我请求它们时异步进行。”

只有一个流,不能复制。

Iterator您通常看到的大多数 s 都超过 a 的事实Collection是偶然的。

于 2010-10-05T08:27:48.937 回答
2

谷歌拥有的唯一原因UnmodifiableIterator是基本上保证其集合的不变性。他们确保您无法更改集合的内部状态。

不要忘记迭代器的最初想法是它是在横向期间指向当前元素的指针,并且它管理到下一个/上一个横向(对于双向链接迭代器的反向)到它的下一个/上一个元素。

迭代器不能克隆并没有真正的原因,很简单,克隆迭代器仍然意味着有一个指向相同集合元素的迭代器(除了它现在位于 2 个不同的地址空间中)。除非您希望克隆的迭代器指向另一个集合,否则没有意义。

于 2010-09-30T15:19:29.083 回答
1

作为为什么要复制迭代器的简单示例,请考虑以下代码,该代码在单个数组中查找第一对匹配值。

for(int i=0;i<size;i++)
{
  x = array[i];

  for(int j=i+1;j<size;j++)
  {
    y = array[j];
    if(x == y)
    {
      doSomething();
      break;
    }
}

注意“j=i+1”。这就是您遇到迭代器问题的地方。哦,好吧,解决方法似乎在 Java 中相当普遍......

于 2011-09-04T22:00:58.147 回答
1

我想要这样的东西,这就是我所做的(基于对 Lambdaj 所做的一些工作)。
主要缺陷是这Iterator基本上会用 Iterator 的所有假定内容填充 a List,这些内容可能在内存中非常重。

为什么我使用列表,因为有时一个Iterator以特定顺序迭代,所以“sub- Iterators”必须做同样的事情(这ListIterator真的对我有帮助)。

public class IterableIterator<T> implements Iterable<T>, Iterator<T> {
    //The content of the given iterator. Will be filled by its iterators.
    private final List<T> iteratorContent = new ArrayList<T>();
    private final Iterator<T> originalIterator;
    private final Iterator<T> innerIterator;

    public IterableIterator(Iterator<T> originalIterator) {
        this(originalIterator, false);
    }

    public IterableIterator(Iterator<T> originalIterator, boolean cache) {
        if (originalIterator == null) {
            throw new IllegalArgumentException("Parameter can't be null");
        }

        this.originalIterator = originalIterator;
        if (cache) {
            while (originalIterator.hasNext()) {
                iteratorContent.add(originalIterator.next());
            }
        }

        innerIterator = iterator();
    }

    @Override
    public Iterator<T> iterator() {
        return new IteratorIterator();
    }

    @Override
    public boolean hasNext() {
        return innerIterator.hasNext();
    }

    @Override
    public T next() {
        return innerIterator.next();
    }

    @Override
    public void remove() {
        innerIterator.remove();
    }

    private class IteratorIterator implements Iterator<T> {
        private ListIterator<T> innerIterator = iteratorContent.listIterator();

        @Override
        public boolean hasNext() {
            return innerIterator.hasNext() || originalIterator.hasNext();
        }

        @Override
        public T next() {
            if (!innerIterator.hasNext() && originalIterator.hasNext()) {
                T item;
                synchronized (originalIterator) {
                    item = originalIterator.next();
                    iteratorContent.add(item);
                }
                innerIterator = iteratorContent.listIterator(innerIterator.nextIndex());
            }
            if (innerIterator.hasNext()) {
                try {
                    return innerIterator.next();
                } catch (ConcurrentModificationException e) {
                    //Quick and dirty solution if you have a concurrent modification.
                    //It can't happen from the outside, so you can easily suppose that another originalIterator
                    //from this class has been called and had added elements to the list.
                    //Best thing to do, reset the originalIterator to the current position.
                    innerIterator = iteratorContent.listIterator(innerIterator.nextIndex());
                    return innerIterator.next();
                }
            }

            throw new NoSuchElementException();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
于 2010-10-05T08:12:57.747 回答
1

您始终可以实现自己的CopyableIterator实现Iterator. 然后你可以做

new CopyableItereator(collection);

班级会是这样的

class CopyableIterator implements Iterator{
Iterator iterator;
Collection collection;
int index=0;

public CopyableIterator(Collection collection){
super();
this.collection = collection;
this.iterator = collection.iterator();
}

public CopyableIterator(Collection collection, int index){
super();
this.collection =collection;
this.iterator = collection.iterator();
this.advanceToIndex(iterator,index); //This function just moves the iterator till the index.
this.index=index;
}

//Override the functions of Iterator here returning iterator.function()

@Override
public Object next(){
index++;
return this.iterator.next();
}

public CopyableIterator copy(){
return new CopyableIterator(this.collection,this.index)

}

}

免责声明:这是大致的课程。它尚未经过测试。

于 2010-09-30T15:20:12.937 回答
0

有什么理由吗?只是为了降低实现它的复杂性?

设计和实现一个Iterator支持操作的包装类会很简单copy。不过,我不确定它通常是否有用,尤其是因为在一般情况下,这将是一项昂贵的操作。仅这一点就足以让 Java 设计人员不想添加copy()Iterator接口中。

跟进

这就是我正在考虑的事情:

public class CopyableIterator<T> implements Iterator<T> {
    private Iterator<T> it;
    private List<T> copy = new ArrayList<T>();
    private int pos;
    public CopyableIterator(Iterator<T> it) {
        while (it.hasNext()) {
            copy.append(it.next());
        }
        this.it = copy.iterator();
    }
    public T next() {
        T res = next();
        pos++;
        return res;
    }
    public boolean hasNext() {
        return it.hasNext();
    }
    public Iterator<T> copy() {
        return copy.sublist(pos, copy.size()).iterator();
    }
    public void remove() {
        throw new UnsupportedOperationException();
    }
}

理由是这样的:

  • 如果我要包装 opaque Iterator,那么我可以复制它的唯一方法是使用next()and读取它并从中hasNext()构造副本Iterator

  • 但在开始使用原始迭代器之前,我必须这样做。

  • 简单的方法是在我开始使用迭代器内容之前制作它的副本。(它可以通过惰性增量复制来完成,但实现可能会变得非常复杂......尤其是当您考虑复制复制的迭代器时。)

另一个答案中提出的方法仅限于普通集合迭代器。如果你有一个包装的迭代器,或者来自其他来源的迭代器(例如)没有实现Iterable,那么你就被烤了。

即使有这个前提条件,上面的方法也不会返回迭代器的真实副本。相反,它为基础集合返回一个新的迭代器。这是一个重要的区别。除非您实际复制迭代的元素引用,否则无法保证迭代器将返回相同的序列。查看Concurrent...集合类型的迭代器的记录行为。

于 2010-09-30T15:16:15.403 回答
0

复制一个 究竟意味着什么Iterator?你的意思是Iterator除了从头开始之外,它应该能够像它自己一样创建一个新的?那是...的责任Iterable...复制该功能是没有意义的,尤其是考虑到迭代器的状态性质...只会使事情变得混乱。

如果你这样写,你会期望发生什么:

Iterator<Foo> iter = someIterable.iterator();
iter.next();
iter.next();
for (Foo foo : iter) {
  ...
}

您是否希望 for 循环遍历迭代器将返回的每个项目,或者除了前两个之外的每个项目?您是否希望在 for 循环完成后迭代器为空?

于 2010-09-30T15:18:24.090 回答
0

ILMTitan 和 Christoffer Hammarström 暗示但没有具体说明复制流可能是不可能的,因为它要求流元素具有复制功能的实现,以便保存可复制迭代器所需的状态。意识到元素可能是可变的(或引用动态值),并且它们可能引用需要自定义复制功能的其他结构和语义。

因此可复制的迭代器与可复制的流元素不正交,所以这就是为什么可复制的迭代器通常是不可能的。

另一个更模糊的原因是复制行为对内存分配和释放有副作用。甚至流元素的复制功能也可能有其他副作用。

另一个原因是编译成汇编语言时可能无法进行一些低级优化。

于 2016-04-22T15:51:54.490 回答
-1

不可能复制一个迭代器——它基本上没有意义。对于某些人来说,这在 Iterator 接口中是显而易见的,但让我们用一个具体的例子来演示它。事实上,让我们用一个关于具体的例子来演示。

带混凝土块的酒吧

这是混凝土条上的混凝土迭代器的图片。在我们的例子中,迭代意味着使用撬棒从棒上断开一块。现在,请注意:

  • 酒吧不是碎片的集合(尽管其中一些有缺陷):我们在迭代时创建碎片。
  • 通过迭代器 (of next()) 的迭代结果永远不会是 bar 的另一个迭代的结果。结果已从中删除。
  • 根据天气、施加的力量或某种热噪声(想想:随机性),迭代可能会产生不同的片段。
  • 通过迭代器 (of next()) 的迭代结果永远不会是条形的另一次迭代的结果 - 因为精确迭代结果的概率空间是连续的,并且没有特定的结果片段具有非零概率度量。

以上任何内容都应该说服您不要尝试“复制迭代器”,这很愚蠢......

于 2015-01-25T21:31:06.770 回答
-1

创建迭代器是为了一次遍历一个集合中的所有对象,使用由所述集合支持的数据。

Iterator<T>几乎总是使用私有内部类实现,该内部类可能使用作为外部类一部分的状态。Iterator因此,如果不编写自己的Collection(或其他),就无法真正修改's 的行为。

复制 Iterator 可能会导致许多新问题,例如与支持集合不同步。

于 2010-09-30T17:13:02.787 回答