我想在Java中实现Iterator
,其行为与Python中的以下生成器函数类似:
def iterator(array):
for x in array:
if x!= None:
for y in x:
if y!= None:
for z in y:
if z!= None:
yield z
java端的x可以是多维数组或某种形式的嵌套集合。我不确定这是如何工作的。想法?
答案 0 :(得分:41)
如果有相同的需要,那就为它写了一个小课。以下是一些例子:
Generator<Integer> simpleGenerator = new Generator<Integer>() {
public void run() throws InterruptedException {
yield(1);
// Some logic here...
yield(2);
}
};
for (Integer element : simpleGenerator)
System.out.println(element);
// Prints "1", then "2".
无限发电机也是可能的:
Generator<Integer> infiniteGenerator = new Generator<Integer>() {
public void run() throws InterruptedException {
while (true)
yield(1);
}
};
Generator
类在内部使用Thread来生成项目。通过覆盖finalize()
,它确保如果不再使用相应的Generator,则不会留下任何线程。
性能显然不是很好,但也不是太破旧。在具有双核i5 CPU @ 2.67 GHz的机器上,可以在&lt; 0.03S。
代码在GitHub上。在那里,您还可以找到有关如何将其作为Maven / Gradle依赖项包含的说明。
答案 1 :(得分:16)
确实Java没有收益,但您现在可以使用Java 8流。 IMO它真的是一个复杂的迭代器,因为它由一个数组支持,而不是一个函数。鉴于它循环中循环中的循环可以表示为使用过滤器的流(跳过空值)和flatMap来流式传输内部集合。它也与Python代码的大小有关。我已将它转换为迭代器,以便在闲暇时使用并打印出来进行演示,但如果您所做的只是打印,则可以使用forEach(System.out :: println)而不是iterator()结束流序列
public class ArrayIterate
{
public static void main(String args[])
{
Integer[][][] a = new Integer[][][] { { { 1, 2, null, 3 },
null,
{ 4 }
},
null,
{ { 5 } } };
Iterator<Object> iterator = Arrays.stream(a)
.filter(ax -> ax != null)
.flatMap(ax -> Arrays.stream(ax)
.filter(ay -> ay != null)
.flatMap(ay -> Arrays.stream(ay)
.filter(az -> az != null)))
.iterator();
while (iterator.hasNext())
{
System.out.println(iterator.next());
}
}
}
我正在撰写关于生成器实现的文章,作为我在{8}的Java 8函数编程和Lambda表达式博客的一部分,这可能会为您提供更多关于将Python生成器函数转换为Java等价物的想法。
答案 2 :(得分:6)
我希望Java具有生成器/产量,但由于它不使用迭代器可能是你最好的选择。
在这个例子中我坚持使用数组,但总的来说我会建议使用Iterable Collection,例如。名单。在示例中,我展示了如何获取数组的迭代器非常容易:
package example.stackoverflow;
import com.sun.xml.internal.xsom.impl.scd.Iterators;
import java.util.Arrays;
import java.util.Iterator;
public class ArrayGenerator<T> implements Iterable<T> {
private final T[][][] input;
public ArrayGenerator(T[][][] input) {
this.input = input;
}
@Override
public Iterator<T> iterator() {
return new Iter();
}
private class Iter implements Iterator<T> {
private Iterator<T[][]> x;
private Iterator<T[]> y;
private Iterator<T> z;
{
x = Arrays.asList(input).iterator();
y = Iterators.empty();
z = Iterators.empty();
}
@Override
public boolean hasNext() {
return z.hasNext() || y.hasNext() || x.hasNext();
}
@Override
public T next() {
while(! z.hasNext()) {
while(! y.hasNext()) {
y = Arrays.asList(x.next()).iterator();
}
z = Arrays.asList(y.next()).iterator();
}
return z.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove not supported");
}
}
public static void main(String[] args) {
for(Integer i :
new ArrayGenerator<Integer>(
new Integer[][][]{
{
{1, 2, 3},
{4, 5}
},
{
{},
{6}
},
{
},
{
{7, 8, 9, 10, 11}
}
}
)) {
System.out.print(i + ", ");
}
}
}
答案 3 :(得分:4)
Java中没有收益,所以你必须自己做所有这些事情,最后得到荒谬的代码:
for(Integer z : new Iterable<Integer>() {
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
final Integer[][][] d3 =
{ { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } },
{ { 10, 11, 12 }, { 13, 14, 15 }, { 16, 17, 18 } },
{ { 19, 20, 21 }, { 22, 23, 24 }, { 25, 26, 27 } } };
int x = 0;
int y = 0;
int z = 0;
@Override
public boolean hasNext() {
return !(x==3 && y == 3 && z == 3);
}
@Override
public Integer next() {
Integer result = d3[z][y][x];
if (++x == 3) {
x = 0;
if (++y == 3) {
y = 0;
++z;
}
}
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}) {
System.out.println(z);
}
但如果你的样本有多个单yield
,那最终会更糟。
答案 4 :(得分:1)
从Python样式的生成器到Java样式的迭代器的转换可以自动化。如果您愿意在构建过程中接受代码生成,那么您可能会对这个为您进行翻译的原型工具感兴趣:
答案 5 :(得分:1)
假设您在问题中描述的Python数据结构可以使用以下Java类型进行描述:
List<List<List<T>>>;
,您想在这样的操作中使用它:
for (T z : iterator(array)) {
// do something with z
}
如果是这样,则可以使用Java 8流轻松实现Python iterator()
:
public <T> Iterable<T> iterator(List<List<List<T>>> array) {
return array.stream()
.filter(Objects::nonNull) // -> emits stream of non-null `x`s
.flatMap(x -> x.stream()).filter(Objects::nonNull) // -> emits […] `y`s
.flatMap(y -> y.stream()).filter(Objects::nonNull) // -> emits […] `z`s
.collect(Collectors.toList()); // get list of non-null `z`s to iterate on
}
当然,您不能收集结果并输出流以进行进一步的流处理(人们告诉我这是个好主意):
public <T> Stream<T> streamContent(List<List<List<T>>> array) {
return array.stream()
.filter(Objects::nonNull) // -> emits stream of non-null `x`s
.flatMap(x -> x.stream()).filter(Objects::nonNull) // -> emits […] `y`s
.flatMap(y -> y.stream()).filter(Objects::nonNull); // -> emits […] `z`s
}
// ...
streamContent(array).forEach(z -> {
// do something with z
});
答案 6 :(得分:0)
游戏很晚了,但我想提供我的解决方案作为参考。 https://github.com/tsi-software/JavaGenerator 一个 Java 类,允许您编写与 Python 和 C# 尽可能类似的“生成器”代码。
答案 7 :(得分:-1)
不,Java本身没有“生成器”或“产量”,但使用Observer Pattern可以获得相同的功能。使用像RxJava这样的现代实现时,这会得到增强。您的代码将订阅Obserable,并且每当它尝试从Observable读取下一个值时,它将导致它“生成”它的下一个值。 Observable可以像Python或JavaScript的生成器一样维护它自己的状态。当没有要读取的新值时,“next()”方法将阻止等待新数据可用。可以找到一个很好的例子HERE。