我使用泛型在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功能。
答案 0 :(得分:36)
您可以返回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
}
};
}
};
}
答案 1 :(得分:20)
“yield return”是一个非常复杂的编译技巧。它基本上允许你声明性地实现IEnumerable,而没有任何令人讨厌的“搞清楚”如何构建迭代器的细节。不幸的是,它并没有很好地转换成其他语言,因为很少有编译器具有这样的能力。在某种程度上,“收益率回报”就像革命一样。
基本上在C#中,编译器将生成IEnumerable和IEnumerator(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!
如果你们都会原谅我的3AM伪java ......
答案 2 :(得分:14)
答案 3 :(得分:5)
我不明白为什么人们在谈论线程......有什么我不知道收益率的回报吗?
据我所知,yield return只保存方法堆栈并在以后恢复它。要实现收益率返回,您只需手动保存状态。有关详细信息,请参阅Java迭代器类,但是对于链接列表,您只需保存当前项即可。对于数组,您只需要索引。
答案 4 :(得分:1)
只是为了帮助读者了解细节。
如果你创建一个包含所有结果元素的新列表并返回列表,那么这是一个很好的实现,很简单,可以编码。您可以根据需要拥有有趣的数据结构,并在扫描正确的条目时,只需返回所有匹配项的列表,您的客户端将在列表中进行迭代。
如果要保存状态,可能会更复杂。每次调用函数时,您都需要到达您曾经去过的地方。更不用说重入问题等了。
带线程的解决方案不会创建新列表。它就像第一个解决方案一样简单。唯一的问题是你涉及一个更难编码的线程同步,并且它的性能会受到惩罚。
所以,是的,收益率很高,并且缺少Java。但有一些解决方法。
答案 5 :(得分:0)
可以将收益退还操作视为
因此,我将其实现为类似于类协程的状态机。 在这种机制内,每个指令都有其指令指针,索引和 指令可能带有标签,因此我们可以使用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());
}
}
}
答案 6 :(得分:0)
这个问题发布以来已经很久了,我不确定要写这么一个老问题的答案,但是实现这个问题的另一种方式已经出现在我身上,我想在这里提出,以防万一鉴于该SO线程是Google最早的命中之一,因此可以进行搜索。
下面显示的代码已经在我的脑海中编译了。绝对不能保证它是正确的,但是背后的想法是。
使用回调
是的,我知道,它与yield return
不同。但是我不认为OP会特别希望替换(可以添加适量的糖)放入for (var x : <some_iterator>)
中。相反,我的方法更类似于C#的linq
(或Java的stream()
),而不是类似yield
的收益。
@FunctionalInterface
public interface Looper<T> {
void each(T item);
}
public interface Loopable<T> {
void forEach(Looper<? super T> looper);
}
然后,您将在代码中实现Loopable<T>
,创建此伪迭代器。确实不是,它只是利用@FunctionalInterface
,这是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);
}
}
}
答案 7 :(得分:-1)
我试图理解产量是多少但没有C#经验我不确定我是否拥有它但我会尝试...
我会建议以下......
Something answer = null;
for (Something author: authors){
if (author.equals("Tom Jones"){
answer = author;
break;
}
}
当从方法返回值时,我将执行以下操作...
public LinkedList<something> getAuthors(LinkedList<something> list){
LinkedList<something> ret = new LinkedList<something>();
for (something s:list){
if (s.equals("abc"))
ret.add(s);
}
return ret;
}
我丢失了情节吗?
答案 8 :(得分:-1)
如果你想要yield return
的全部功能,你可能需要在两个线程中设置它 - 一个用于第一个方法,一个用于第二个方法。然后第一个线程应该wait
,直到第二个线程将其值放在可访问的位置,并notify
它已准备就绪。然后第一个线程将处理该值wait
以获取下一个值,等等。
答案 9 :(得分:-2)
使用我的java库来实现yield return而不使用线程或字节码操作
答案 10 :(得分:-30)
我在这里遗漏了什么吗?已经有java.util.LinkedList,它是完全泛型的,它有一个返回迭代器的方法。
如果你真的想重新发明轮子,我建议你研究创建一个LinkedListIterator类,可能实现了ListIterator。它会记住它在链表中的当前位置,并在每次连续呼叫时推进它。