简化 Java 中自定义迭代器的编写
在 Java 中为自定义集合编写迭代器非常复杂,因为您实际上必须编写一个状态机,而不是编写提供一个又一个元素的直接代码:
public class CustomCollection<T> implements Iterable<T>
{
private T[] data;
private int size;
@Override
public Iterator<T> iterator()
{
return new Iterator<T>()
{
private int cursor = 0;
@Override
public boolean hasNext()
{
return cursor < size;
}
@Override
public T next()
{
return data[cursor++];
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
// ...
}
对于比数组列表或链表更复杂的集合,获取正确地处理这些状态机是一项艰巨的任务。事实上,C# 设计团队认为编写自定义迭代器足够复杂,需要引入特殊语言支持 (yield return
) 来让编译器构建状态机。
Java 的下一版本中是否会出现类似 yield return
的内容?或者是否有任何库解决方案可以让我在用 Java 编写自己的迭代器时变得更轻松?
Writing iterators for custom collections in Java is quite complicated, because instead of writing straight-forward code that provides one element after the other, you essentially have to write a state machine:
public class CustomCollection<T> implements Iterable<T>
{
private T[] data;
private int size;
@Override
public Iterator<T> iterator()
{
return new Iterator<T>()
{
private int cursor = 0;
@Override
public boolean hasNext()
{
return cursor < size;
}
@Override
public T next()
{
return data[cursor++];
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
// ...
}
For collections more complicated than an array list or a linked list, getting these state machines correctly is a daunting task. In fact, the C# design team deemed writing custom iterators complicated enough to introduce special language support (yield return
) for letting the compiler build the state machines.
Is something like yield return
coming in the next version of Java? Or are there any library solutions that make my life easier when it comes to writing my own iterators in Java?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不,Java 没有像
yield
这样的东西。就库而言,Guava 有许多有用的类,可以使某些类型的迭代器易于编写:TcomputeNext()
方法。AbstractLinkedIterator
要求您实现TcomputeNext(T previous)
。AbstractIterator
可以用于此目的,如下所示:您也可以按照 Amir 的建议使用
Arrays.asList
,甚至可以执行如下操作:No, Java doesn't have anything like
yield
. As far as libraries, Guava has a number of helpful classes to make certain kinds of iterators easy to write:AbstractIterator
just requires you to implement aT computeNext()
method.AbstractLinkedIterator
requires you to implementT computeNext(T previous)
.AbstractIterator
could be used for this as follows:You could also use
Arrays.asList
as Amir suggested, or even do something like this:也许我只是不明白你的问题。你能不能
return Arrays.asList(data).iterator()
Maybe I am just not understanding your questions. Can you not do
return Arrays.asList(data).iterator()
如果您不介意启动一个新线程,可以使用 同步队列
if you don't mind starting a new thread it's possible using a SynchronousQueue
Java 始终提供一种机制来维护状态并在稍后的某个时间点继续执行:线程。我的库解决方案的基本思想是让 ConcurrentIterable 在一个线程中生成元素,并让 ConcurrentIterator 消耗元素em> 他们在另一个中,通过有界队列进行通信。 (这通常称为生产者/消费者模式。)
首先,这里是简化用法的演示:
请注意完全没有状态机。您所要做的就是从
ConcurrentIterable
派生并实现provideElements
方法。在此方法中,您可以编写直接的代码,为集合中的每个元素调用provideElement
。有时,客户端不会迭代整个集合,例如在线性搜索中。一旦检测到中止,您就可以通过检查
iterationAborted()
来停止提供元素:只要您不关心,不检查
iterationAborted()
就完全可以了关于正在生成的附加元素。对于无限序列,必须检查iterationAborted()。生产者如何检测消费者已经停止迭代?这是通过在消费者中对令牌进行强引用并在生产者中对同一令牌进行弱引用来实现的。当消费者停止迭代时,令牌就有资格进行垃圾回收,并且最终对生产者来说将变得不可见。从那时起,所有新元素都将被丢弃。
(如果没有这种预防措施,在某些情况下,有界队列最终可能会被填满,生产者将进入无限循环,并且所包含的元素永远不会被垃圾收集。)
现在介绍实现细节:
ConcurrentIterable.java
CommunicationChannel.java
ConcurrentIterator。 java
Option.java
让我知道你的想法!
Java has always provided a mechanism for maintaining state and continuing execution at a later point in time: threads. The basic idea for my library solution is to let a
ConcurrentIterable
produce the elements in one thread, and let aConcurrentIterator
consume them in another, communicating via a bounded queue. (This is generally known as the producer/consumer pattern.)First, here is a demonstration of the simplified usage:
Note the complete absence of state machines. All you have to do is derive from
ConcurrentIterable
and implement the methodprovideElements
. Inside this method, you write straight-forward code which callsprovideElement
for each element in the collection.Sometimes a client does not iterate through the entire collection, for example in a linear search. You can stop providing elements as soon as an abortion is detected by checking
iterationAborted()
:It is perfectly fine not to check
iterationAborted()
, as long as you do not care about the additional elements being generated. With infinite sequences, checkingiterationAborted()
is mandatory.How can the producer detect that the consumer has stopped iterating? This is implemented by having a strong reference to a token in the consumer and a weak reference to that same token in the producer. When the consumer stops iterating, the token becomes eligible for garbage collection, and it will eventually become invisible to the producer. From then on, all new elements will simply be discarded.
(Without this precaution, under certain circumstances the bounded queue could eventually fill up, the producer would enter an infinite loop, and the contained elements would never be garbage collected.)
And now for the implementation details:
ConcurrentIterable.java
CommunicationChannel.java
ConcurrentIterator.java
Option.java
Let me know what you think!