我想在 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可以是多维数组或某种形式的嵌套集合。我不确定这将如何运作。有想法吗?
有同样的需求,所以为此写了一个小课程。以下是一些示例:
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
类在内部与线程一起工作来生成项目。通过覆盖 finalize()
,它可以确保在不再使用相应的生成器时不会留下任何线程。
性能显然不是很好,但也不算太差。在我的双核 i5 CPU @ 2.67 GHz 的机器上,可以在 < 0.03s.
生产 1000 件商品代码位于 GitHub 上。在那里,您还可以找到有关如何将其作为 Maven/Gradle 依赖项包含在内的说明。
Java确实没有yield,但你现在可以使用Java 8流。在我看来,它确实是一个复杂的迭代器,因为它由数组而不是函数支持。鉴于它是一个循环中的循环,可以使用过滤器(跳过空值)和 flatMap 来表示为 Stream 来流式传输内部集合。它还与 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());
}
}
}
我正在撰写关于生成器实现的文章,作为我关于 Java 8 函数式编程和 Lambda 表达式的博客的一部分,网址为 http://thecannycoder.wordpress.com/,这可能会给您更多关于将 Python 生成器函数转换为 Java 等价物的想法.
我希望 Java 有生成器/yield,但因为它不使用迭代器可能是你最好的选择。
在这个例子中我坚持使用数组,但一般来说我建议使用 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 + ", ");
}
}
}
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
,结果会更糟。
从 Python 风格的生成器到 Java 风格的迭代器的转换可以自动化。如果您愿意在构建过程中接受代码生成,您可能会对这个为您进行翻译的原型工具感兴趣:
假设您在问题中描述的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
});
虽然很晚了,但我想提供我的解决方案作为参考。 https://github.com/tsi-software/JavaGenerator 一个 Java 类,允许您尽可能类似于 Python 和 C# 编写“生成器”代码。
使用新的
库中的
Seq
,它已经在Java中实现了生成器,您可以像在Python中一样编写自己的生成器函数
public Seq<Integer> generate(List<List<List<Integer>>> array) {
return c -> {
for (List<List<Integer>> x : array) {
if (x != null) {
for (List<Integer> y : x) {
if (y != null) {
for (Integer z : y) {
if (z != null) {
c.accept(z);
}
}
}
}
}
}
};
}
然后你可以像普通的Java
seq
一样操作/收集返回的stream
。
您可以使用流的迭代器来完成此操作。
// Save the iterator of a stream that generates fib sequence
Iterator<Integer> myGenerator = Stream
.iterate(new Integer[]{ 1, 1 }, x -> new Integer[] { x[1], x[0] + x[1] })
.map(x -> x[0]).iterator();
// Print the first 5 elements
for (int i = 0; i < 5; i++) {
System.out.println(myGenerator.next());
}
System.out.println("done with first iteration");
// Print the next 5 elements
for (int i = 0; i < 5; i++) {
System.out.println(myGenerator.next());
}
输出:
1
1
2
3
5
done with first iteration
8
13
21
34
55