web-dev-qa-db-fra.com

Fonctions de générateur équivalentes à Java

Je voudrais implémenter un Iterator dans Java qui se comporte un peu comme la fonction de générateur suivante en 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

x du côté Java côté peut être un tableau multidimensionnel ou une forme de collection imbriquée. Je ne sais pas comment cela fonctionnerait. Des idées?

37
Eqbal

Avait le même besoin alors j'ai écrit un petit cours pour ça. Voici quelques exemples:

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".

Des générateurs infinis sont également possibles:

Generator<Integer> infiniteGenerator = new Generator<Integer>() {
    public void run() throws InterruptedException {
        while (true)
            yield(1);
    }
};

La classe Generator fonctionne en interne avec un thread pour produire les éléments. En redéfinissant finalize(), il garantit qu'aucun thread ne reste autour si le générateur correspondant n'est plus utilisé.

La performance n'est évidemment pas géniale mais pas trop minable non plus. Sur ma machine avec un processeur i5 dual core à 2,67 GHz, 1000 éléments peuvent être produits en <0,03 s.

Le code est activé GitHub . Vous y trouverez également des instructions sur la façon de l'inclure en tant que dépendance Maven/Gradle.

42
Michael Herrmann

En effet Java n'a pas de rendement, mais vous pouvez maintenant utiliser Java 8 flux. IMO c'est vraiment un itérateur compliqué car il est soutenu par un tableau, pas une fonction). Étant donné que c'est une boucle dans une boucle dans une boucle peut être exprimée comme un flux en utilisant un filtre (pour ignorer les valeurs nulles) et flatMap pour diffuser la collection interne. Il s'agit également de la taille du code Python Je l'ai converti en un itérateur pour l'utiliser à votre guise et imprimé pour le démontrer, mais si tout ce que vous faisiez était l'impression, vous pouvez terminer la séquence de flux avec forEach (System.out :: println) au lieu d'itérateur ().

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());
        }
    }
}

J'écris sur la mise en œuvre de générateurs dans le cadre de mon blog sur Java 8 Functional Programming and Lambda Expressions at http://thecannycoder.wordpress.com/ qui pourrait vous donner quelques idées supplémentaires pour convertir les fonctions du générateur Python en équivalents Java équivalents.

17
TheCannyCoder

Je souhaite Java avait un générateur/rendement, mais comme il n'utilise pas d'itérateurs, c'est probablement votre meilleur choix.

Dans cet exemple, je suis resté avec des tableaux, mais en général, je conseillerais d'utiliser plutôt Iterable Collection, par exemple. Liste. Dans l'exemple, je montre comment il est assez facile d'obtenir des itérateurs pour les tableaux:

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 + ", ");
        }
    }
}
6
Spen

Il n'y a pas de rendement en Java, vous devez donc faire toutes ces choses pour vous-même, avec un code ridicule comme celui-ci:

    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);
    }

Mais si votre échantillon avait plus d'un seul yield, cela finirait encore pire.

4
Arne

La traduction des générateurs de style Python en itérateurs de style Java peut être automatisée. Si vous êtes prêt à accepter la génération de code dans votre processus de construction, vous pourriez être intéressé par cet outil prototype qui effectue la traduction pour vous:

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

1
Calvin