Skip to content Skip to sidebar Skip to footer

Generator Functions Equivalent In Java

I would like to implement an Iterator in Java that behaves somewhat like the following generator function in Python: def iterator(array): for x in array: if x!= None:

Solution 1:

Had the same need so wrote a little class for it. Here are some examples:

Generator<Integer> simpleGenerator = newGenerator<Integer>() {
    publicvoid run() throws InterruptedException {
        yield(1);
        // Some logic here...yield(2);
    }
};
for (Integer element : simpleGenerator)
    System.out.println(element);
// Prints "1", then "2".

Infinite generators are also possible:

Generator<Integer> infiniteGenerator = newGenerator<Integer>() {
    publicvoid run() throws InterruptedException {
        while (true)
            yield(1);
    }
};

The Generator class internally works with a Thread to produce the items. By overriding finalize(), it ensures that no Threads stay around if the corresponding Generator is no longer used.

The performance is obviously not great but not too shabby either. On my machine with a dual core i5 CPU @ 2.67 GHz, 1000 items can be produced in < 0.03s.

The code is on GitHub. There, you'll also find instructions on how to include it as a Maven/Gradle dependency.

Solution 2:

Indeed Java has no yield, but you can now use Java 8 streams. IMO it's really a complicated iterator since it's backed by an array, not a function. Given it's a loop in a loop in a loop can be expressed as a Stream using filter (to skip the nulls) and flatMap to stream the inner collection. It's also about the size of the Python code. I've converted it to an iterator to use at your leisure and printed to demonstrate, but if all you were doing was printing, you could end the stream sequence with forEach(System.out::println) instead of iterator().

publicclassArrayIterate{
    publicstaticvoid main(String args[])
    {
        Integer[][][] a = newInteger[][][] { { { 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());
        }
    }
}

I'm writing about implementation of generators as part of my blog on Java 8 Functional Programming and Lambda Expressions at http://thecannycoder.wordpress.com/ which might give you some more ideas for converting Python generator functions into Java equivalents.

Solution 3:

I wish Java had generator/yield, but since it doesn't using Iterators is probably your best bet.

In this example I stuck with arrays, but in general I would advise using Iterable Collection instead, eg. List. In the example I show how it's pretty easy to get iterators for arrays though:

package example.stackoverflow;

import com.sun.xml.internal.xsom.impl.scd.Iterators;

import java.util.Arrays;
import java.util.Iterator;

publicclassArrayGenerator<T> implementsIterable<T> {
    privatefinal T[][][] input;

    publicArrayGenerator(T[][][] input) {
        this.input = input;
    }


    @Overridepublic Iterator<T> iterator() {
        returnnewIter();
    }

    privateclassIterimplementsIterator<T> {
        private Iterator<T[][]> x;
        private Iterator<T[]> y;
        private Iterator<T> z;

        {
            x = Arrays.asList(input).iterator();
            y = Iterators.empty();
            z = Iterators.empty();
        }

        @OverridepublicbooleanhasNext() {
            return z.hasNext() || y.hasNext() || x.hasNext();
        }

        @Overridepublic T next() {
            while(! z.hasNext()) {
                while(! y.hasNext()) {
                    y = Arrays.asList(x.next()).iterator();
                }
                z = Arrays.asList(y.next()).iterator();
            }
            return z.next();
        }

        @Overridepublicvoidremove() {
            thrownewUnsupportedOperationException("remove not supported");
        }
    }

    publicstaticvoidmain(String[] args) {
        for(Integer i :
                newArrayGenerator<Integer>(
                        newInteger[][][]{
                          {
                            {1, 2, 3},
                            {4, 5}
                          },
                          {
                            {},
                            {6}
                          },
                          {
                          },
                          {
                            {7, 8, 9, 10, 11}
                          }
                        }
                )) {
            System.out.print(i + ", ");
        }
    }
}

Solution 4:

There is no yield in Java, so you have to do all these things for yourself, ending up with ridiculous code as this one:

for(Integer z : newIterable<Integer>() {

        @Override
        publicIterator<Integer> iterator() {

            returnnewIterator<Integer>() {

                finalInteger[][][] 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
                publicboolean hasNext() {
                    return !(x==3 && y == 3 && z == 3);
                }

                @Override
                publicInteger next() {
                    Integer result = d3[z][y][x];
                    if (++x == 3) {
                        x = 0;
                        if (++y == 3) {
                            y = 0;
                            ++z;
                        }
                    }
                    return result;
                }

                @Override
                publicvoid remove() {
                    thrownew UnsupportedOperationException();
                }
            };
        }
    }) {
        System.out.println(z);
    }

But if your sample would have more than one single yield it would end up even worse.

Solution 5:

The translation from Python-style generators to Java-style iterators can be automated. If you're willing to accept code generation in your build process, you might be interested in this prototype tool that does the translation for you:

https://github.com/Calvin-L/gen2it

Post a Comment for "Generator Functions Equivalent In Java"