Java中的收益回报
Posted
技术标签:
【中文标题】Java中的收益回报【英文标题】:Yield Return In Java 【发布时间】:2011-01-22 02:00:43 【问题描述】:我在 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()
编辑:请注意,我对使用任何 3rd 方 API 不感兴趣。仅内置 java 功能。
【问题讨论】:
***.com/questions/1980953/… 我不懂 C#。好奇,收益回报有什么作用? 检查这个:msdn.microsoft.com/en-us/library/9k7k7cf0(VS.80).aspx 过多以编译器为中心?如果我想我必须自己编写编译器为我编写的所有东西...... @MerlynMorgan-Graham 或者当计算(生成器函数)很昂贵并且需要惰性评估时。 【参考方案1】:您可以返回 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
;
;
【讨论】:
【参考方案2】:“yield return”是一个非常复杂的编译器技巧。它基本上可以让您以声明方式实现 IEnumerable,而无需“弄清楚”如何构建迭代器的任何烦人的细节。不幸的是,它不能很好地翻译成其他语言,因为很少有编译器有这样的能力。在某些方面,“收益回报”与革命性一样可怕。
基本上在 C# 中,编译器会生成 IEnumerable 和 IEnumerator (of T) 的两个实现。它通过基本上将您的“方法”的局部变量实现为生成的实现类中的实例字段以及检查包含“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!
如果你们都原谅我凌晨 3 点的伪 java...
【讨论】:
【参考方案3】:试试这个
com.infomancers.collections.yield也可以查看这篇文章以获取示例实现:
Implementation details for Java Yielder【讨论】:
【参考方案4】:我不明白为什么人们在谈论线程......关于收益回报有什么我不知道的吗?
据我了解,yield return 只是保存方法堆栈并在以后恢复它。要实现收益返回,您只需手动保存状态。有关详细信息,请参阅 Java 迭代器类,但对于链接列表,您可以直接保存当前项目。对于数组,您只需要索引。
【讨论】:
这是正确的。 Yield 和 yield return 不使用 C# 中的线程。他们进行编译时转换并创建状态机,但该状态机不使用任何额外的线程(尽管它可能是线程安全的)。【参考方案5】:只是为了帮助读者理解小细节。
如果您创建一个包含所有结果元素的新列表并返回该列表,那么这是一个很好的实现,代码足够简单。您可以根据需要拥有任何有趣的数据结构,并且在扫描它以查找正确的条目时,只需返回所有匹配项的列表,您的客户端将在列表上进行迭代。
如果你想保存一个状态,它可能会更复杂。每次调用函数时,您都需要到达您所在的位置。更不用说重入问题等了。
带有线程的解决方案不会创建新列表。它就像第一个解决方案一样简单。唯一的问题是你涉及到一个线程同步,这有点难以编码,并且有性能损失。
所以,是的,yield return 很棒,Java 中没有。不过也有解决方法。
【讨论】:
【参考方案6】:一个yield返回操作可视为
-
在那里放置一些检查点
在某处写一个值
获取简历后,跳转到旁边的指令。
因此,我将它实现为类似状态机的类,协程。 在这种机制中,每条指令都有其指令指针、索引和 指令可能带有标签,因此我们可以使用 jmp(label) 跳转到标签。
-
添加一些机制来实现 goto 语法:addInstruction(..) 和 jmp()
并将状态/变量存储在某处:setVariable(name,value)、yield(value)
一种暂时挂起/恢复的方法:exec()
例如:
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());
见FibonacciCoroutine.java
回到你的问题...
LinkedListObject<something> current = first;
While (current != null)
yield return current.getValue();
current = current.getNext()
可以转换成下面的代码
//some where in class, or use Var<> to wrap it.
Var<LinkedListObject<something> > current = new Var<>(first);
Coroutine cor = new Coroutine();
cor.While((ins)->current.get() != null).run((ins)->
ins.addInstruction((c)->c.yield(current.get().getValue()) );
// wrap it with lambda for being a checkpoint
ins.addInstruction( (c)->current.set(current.get().getNext()) );
);
所以我们可以使用它的 getYieldValue() 来检索结果,或者简单地调用 coroutine.iterator 将协程转换为迭代器
【讨论】:
我也实现了一个递归例子BinaryTreeCoroutine【参考方案7】:自发布此问题以来已经有很长时间了,我很不确定是否要为这么老的问题写答案,但是我想到了另一种实现此目标的方法,我想在这里展示它以防它对任何人有所帮助搜索这个,因为这个 SO 线程是 Google 中最早的热门话题之一。
下面显示的代码已在我的脑海中编译。绝对不能保证它是正确的,但它背后的想法是。
使用回调
是的,我知道,它与yield return
不同。但我不认为 OP 特别想要一个替代品,可以(用适量的糖)放入for (var x : <some_iterator>)
。相反,我的方法更类似于 C# 的 linq
(或 Java 的 stream()
),而不是 yield
ed 返回。
@FunctionalInterface
public interface Looper<T>
void each(T item);
public interface Loopable<T>
void forEach(Looper<? super T> looper);
然后你将在你的代码中实现Loopable<T>
,创建这个伪迭代器。其实不是,它只是利用@FunctionalInterface
s,这是Java 的回调方式(sorta)
public class WhatEvs implements Loopable<WhatEvs>
// ...
@Override
public void forEach(Looper<? super T> looper)
while(your_condition)
WhatEvs nextItem = getNextItem();
looper.each(nextItem);
【讨论】:
【参考方案8】:如果您想要yield return
的全部功能,您可能需要在两个线程中进行设置——一个用于第一种方法,一个用于第二种方法。然后第一个线程应该wait
直到第二个线程将它的值放在某个可访问的地方并且notify
s 它已经准备好了。然后第一个线程将处理该值,wait
处理下一个值,等等。
【讨论】:
【参考方案9】:使用我的java库实现yield return,不使用线程或字节码操作
http://www.heinerkuecker.de/YieldReturnForNested.html
【讨论】:
该页面为德语,您的答案为英语。请考虑添加您的库如何工作的代码示例。以上是关于Java中的收益回报的主要内容,如果未能解决你的问题,请参考以下文章