Javaジェネリックを使用して)リンクリストを作成しましたが、リスト内のすべての要素を反復処理できるようにしたいと思います。C#では、内部でyield return
を使用しますリストに含まれる要素のリストを移動する際のリンクリスト。
Java上記のバージョンを作成して、リンクリストに含まれるすべてのアイテムを反復処理できるようにするにはどうすればよいですか?
私はコードalaを書くことができるように探しています
LinkedList<something> authors = new LinkedList<something>();
for (Iterator<something> i = authors.Values ; i.HasNext())
doSomethingWith(i.Value);
値の「プロパティ」/メソッドは、次のようなコードで構成されると考えていました
LinkedListObject<something> current = first;
While (current != null){
yield return current.getValue();
current = current.getNext()
}
編集:サードパーティのAPIの使用には興味がないことに注意してください。組み込みJava機能のみ。
Iterableの匿名の実装を返すことができます。効果はかなり似ていますが、これははるかに冗長です。
public Iterable<String> getStuff() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
@Override
public boolean hasNext() {
// TODO code to check next
}
@Override
public String next() {
// TODO code to go to next
}
@Override
public void remove() {
// TODO code to remove item or throw exception
}
};
}
};
}
「yield return」は非常に洗練されたコンパイラトリックです。それは基本的にあなたがあなたの反復子をどのように構築するかを「理解する」という煩わしい詳細なしにIEnumerableを宣言的に実装することを可能にします。残念なことに、このような機能を持つコンパイラはほとんどないため、他の言語にうまく変換できません。ある意味では、「利回り」は革命的なものと同じくらいのろいです。
基本的にC#では、コンパイラーはIEnumerableとIEnumerator(Tの)の2つの実装を生成します。これは基本的に、「メソッド」のローカル変数を、生成された実装クラスのインスタンスフィールドとして実現し、「yield return」アーティファクトを含むフレームを調べることによって行われます。これがわかったら、十分に丸みを帯びた開発者が同じことを明示的に行うことができるはずです...簡潔ではありませんが。実演するために、CONCAT!
public static <T> Iterable<T> concat(Iterable<T> x, Iterable<T> y)
{
for(T e: x)
{
yield return e;
}
for(T e: y)
{
yield return e;
}
}
// becomes ....
public static <E> Iterator<E> concat_(Iterable<E> x, Iterator<E> y)
{
T e1, e2;
Iterator<E> i1, i2;
Iterator<E> s;
Iterator<E> s4 = new Iterator<E>()
{
public bool hasNext()
{
return false;
}
public E next()
{
throw ... ;
}
public void remove()
{
throw ... ;
}
}
Iterator<E> s3 = new Iterator<E>()
{
Iterator<E> act()
{
if(i2.hasNext())
{
return i2;
}
i2 = y.iterator();
return (s = s4);
}
public bool hasNext()
{
return act().hasNext();
}
public E next()
{
return act().next();
}
public void remove()
{
return i2.remove();
}
}
Iterator<E> s2 = new Iterator<E>()
{
Iterator<E> act()
{
if(i1.hasNext())
{
return i1;
}
i2 = y.iterator();
return (s = s3);
}
public bool hasNext()
{
return act().hasNext();
}
public E next()
{
return act().next();
}
public void remove()
{
return i1.remove();
}
};
Iterator<E> s1 = new Iterator<E>()
{
Iterator<E> act()
{
i1 = x.iterator();
return s = s2;
}
public bool hasNext()
{
return act().hasNext();
}
public E next()
{
return act().next();
}
public void remove()
{
return act().remove();
}
};
s = s1;
return new Iterator<T>()
{
public bool hasNext()
{
return s.hasNext();
}
public E next()
{
return s.next();
}
public void remove()
{
return s.remove();
}
};
}
public static <T> Iterable<T> concat(Iterable<T> x, Iterable<T> y)
{
return new Iterable<T>()
{
public Iterator<T> iterator()
{
return concat_(x, y)
}
};
}
// tada!
皆さんが私の3AM疑似Javaを許してくれるなら...
なぜ人々がスレッドについて話しているのか理解できません...イールドリターンについて私が知らないことはありますか?
私の理解では、yield returnはメソッドスタックを保存し、後で復元するだけです。利回りを実装するには、手動で状態を保存する必要があります。詳細については、Javaイテレータクラスを参照してください。ただし、リンクリストの場合は、現在のアイテムを保存するだけで十分です。配列の場合は、インデックスが必要です。
読者が細部を理解するのを助けるためだけに。
すべての結果の要素を含む新しいリストを作成してリストを返す場合、これは優れた実装であり、コーディングするのに十分簡単です。必要なだけ興味深いデータ構造を持つことができます。正しいエントリをスキャンするときに、すべての一致のリストを返すだけで、クライアントはリストを反復処理します。
状態を保存する場合は、さらに複雑になる可能性があります。関数が呼び出されるたびに、どこにいたかを知る必要があります。リエントラントな問題などは言うまでもありません.
スレッドを使用したソリューションでは、新しいリストは作成されません。そして、それは最初のソリューションと同じくらい簡単です。唯一の問題は、コーディングが少し難しく、パフォーマンスが低下するスレッド同期を伴うことです。
だから、はい、イールドリターンは素晴らしく、Javaにはありません。まだ回避策があります。
イールドリターン操作は、
したがって、私はコルーチンというクラスのような状態マシンとして実装します。このメカニズム内では、各命令にはその命令ポインタ、インデックスがあり、命令にはラベルが付いている場合があるため、jmp(label)を使用してラベルにジャンプできます。
例えば:
public class FibbonaciCoroutine implements Iterator<BigInteger> {
BigInteger[] bucket = { new BigInteger("1"), new BigInteger("1"), new BigInteger("0") };
int idx = 2;
Coroutine coroutine = new Coroutine((pthis) -> {
pthis.addInstruction("_label1", (me) -> {
int p1 = idx - 2;
int p2 = idx - 1;
if (p1 < 0)
p1 += 3;
if (p2 < 0)
p2 += 3;
bucket[idx] = bucket[p1].add(bucket[p2]);
idx = (idx + 1) % bucket.length;
me.yield(bucket[idx]);
});
// goto
pthis.addInstruction((me) -> {
me.jmp("_label1");
});
pthis.start();
});
@Override
public boolean hasNext() {
return !coroutine.isStopped();
}
@Override
public BigInteger next() {
while (coroutine.exec())
;
return coroutine.getYieldValue();
}
public static void main(String[] argv) {
FibbonaciCoroutine cor = new FibbonaciCoroutine();
for (int i = 0; i < 100 && cor.hasNext(); ++i) {
System.out.printf("%d ", cor.next());
}
}
}