web-dev-qa-db-fra.com

Compréhension de liste de type python en Java

Puisque Java n'autorise pas le passage de méthodes en tant que paramètres, quelle astuce utilisez-vous pour implémenter Python comme la compréhension de liste en Java?

J'ai une liste (ArrayList) de chaînes. Je dois transformer chaque élément en utilisant une fonction afin d'obtenir une autre liste. J'ai plusieurs fonctions qui prennent une chaîne en entrée et renvoient une autre chaîne en sortie. Comment créer une méthode générique à laquelle on peut attribuer la liste et la fonction en tant que paramètres, de manière à pouvoir obtenir une liste à chaque élément traité. Ce n'est pas possible au sens littéral, mais quelle astuce devrais-je utiliser?

L’autre option est d’écrire une nouvelle fonction pour chaque plus petite fonction de traitement de chaîne, qui passe en boucle sur toute la liste, ce qui n’est pas très cool.

53
euphoria83

Fondamentalement, vous créez une interface de fonction:

public interface Func<In, Out> {
    public Out apply(In in);
}

puis transmettez une sous-classe anonyme à votre méthode.

Votre méthode peut soit appliquer la fonction à chaque élément sur place:

public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) {
    ListIterator<T> itr = list.listIterator();
    while (itr.hasNext()) {
        T output = f.apply(itr.next());
        itr.set(output);
    }
}
// ...
List<String> myList = ...;
applyToListInPlace(myList, new Func<String, String>() {
    public String apply(String in) {
        return in.toLowerCase();
    }
});

ou créez une nouvelle List (créant essentiellement un mappage de la liste d'entrée à la liste de sortie):

public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) {
    List<Out> out = new ArrayList<Out>(in.size());
    for (In inObj : in) {
        out.add(f.apply(inObj));
    }
    return out;
}
// ...
List<String> myList = ...;
List<String> lowerCased = map(myList, new Func<String, String>() {
    public String apply(String in) {
        return in.toLowerCase();
    }
});

Lequel est préférable dépend de votre cas d'utilisation. Si votre liste est extrêmement longue, la solution en place peut être la seule solution viable. si vous souhaitez appliquer plusieurs fonctions différentes à la même liste d'origine pour créer de nombreuses listes dérivées, vous souhaiterez la version map.

35
Michael Myers

En Java 8, vous pouvez utiliser des références de méthodes:

List<String> list = ...;
list.replaceAll(String::toUpperCase);

Ou, si vous voulez créer une nouvelle instance de liste:

List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
43
yurez

La bibliothèque Google Collections contient de nombreuses classes permettant de travailler avec des collections et des itérateurs à un niveau beaucoup plus élevé que celui pris en charge par Java, et de manière fonctionnelle (filtre, carte, repli, etc.). Il définit les interfaces et les méthodes des fonctions et des prédicats qui les utilisent pour traiter les collections de manière à ne pas être obligés de le faire. Il comporte également des fonctions pratiques qui simplifient l'utilisation des génériques Java. 

J'utilise aussi Hamcrest ** pour filtrer les collections.

Les deux bibliothèques sont faciles à combiner avec des classes d'adaptateur.


** Déclaration d'intérêt: j'ai co-écrit Hamcrest

17
Nat

Apache Commons CollectionsUtil.transform (Collection, Transformer) est une autre option.

5
user111246

Je construis ce projet pour écrire la compréhension de liste en Java, est maintenant une preuve de concept dans https://github.com/farolfo/list-comprehension-in-Java

Exemples

// { x | x E {1,2,3,4} ^ x is even }
// gives {2,4}

Predicate<Integer> even = x -> x % 2 == 0;

List<Integer> evens = new ListComprehension<Integer>()
    .suchThat(x -> {
        x.belongsTo(Arrays.asList(1, 2, 3, 4));
        x.is(even);
    });
// evens = {2,4};

Et si nous voulons transformer l’expression de sortie d’une certaine manière comme

// { x * 2 | x E {1,2,3,4} ^ x is even }
// gives {4,8}

List<Integer> duplicated = new ListComprehension<Integer>()
    .giveMeAll((Integer x) -> x * 2)
    .suchThat(x -> {
        x.belongsTo(Arrays.asList(1, 2, 3, 4));
        x.is(even);
    });
// duplicated = {4,8}
1
farolfo

Vous pouvez utiliser lambdas pour la fonction, comme ceci:

class Comprehension<T> {
    /**
    *in: List int
    *func: Function to do to each entry
    */
    public List<T> comp(List<T> in, Function<T, T> func) {
        List<T> out = new ArrayList<T>();
        for(T o: in) {
            out.add(func.apply(o));
        }
        return out;
    }
}

l'usage:

List<String> stuff = new ArrayList<String>();
stuff.add("a");
stuff.add("b");
stuff.add("c");
stuff.add("d");
stuff.add("cheese");
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String>
    a.equals("a")? "1": 
            (a.equals("b")? "2": 
                (a.equals("c")? "3":
                    (a.equals("d")? "4": a
    )))
});

retournera:

["1", "2", "3", "4", "cheese"]
0
MrYurihi redstone