Java中的收益率回报率

时间:2010-02-28 19:51:45

标签: c# java linked-list

我使用泛型在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功能。

11 个答案:

答案 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)

可以将收益退还操作视为

  1. 在此处放置一些检查点
  2. 在某个地方写一个值
  3. 获取简历后,跳至其旁边的指令。

因此,我将其实现为类似于类协程的状态机。 在这种机制内,每个指令都有其指令指针,索引和 指令可能带有标签,因此我们可以使用jmp(label)跳转到标签。

  1. 添加一些实现goto语法的机制:addInstruction(..)和jmp()
  2. 并将状态/变量存储在某个位置:setVariable(name,value),yield(value)
  3. 一种临时挂起/恢复的方法: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

答案 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而不使用线程或字节码操作

http://www.heinerkuecker.de/YieldReturnForNested.html

答案 10 :(得分:-30)

我在这里遗漏了什么吗?已经有java.util.LinkedList,它是完全泛型的,它有一个返回迭代器的方法。

如果你真的想重新发明轮子,我建议你研究创建一个LinkedListIterator类,可能实现了ListIterator。它会记住它在链表中的当前位置,并在每次连续呼叫时推进它。