web-dev-qa-db-fra.com

Google Guava "Zip" deux listes

Avec Google Guava (Google Commons), existe-t-il un moyen de fusionner deux listes de tailles égales en une seule liste, la nouvelle liste contenant les objets composites des deux listes d'entrée?

Exemple:

public class Person {
    public final String name;
    public final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return "(" + name + ", " + age + ")";
    }
}

et

List<String> names = Lists.newArrayList("Alice", "Bob", "Charles");
List<Integer> ages = Lists.newArrayList(42, 27, 31);

List<Person> persons =
    transform with a function that converts (String, Integer) to Person
System.out.println(persons);

Serait sortie:

[(Alice, 42), (Bob, 27), (Charles, 31)]
18
Steve Kuo

A partir de Guava 21, cela est possible via Streams.Zip() :

List<Person> persons = Streams.Zip(names.stream(), ages.stream(), Person::new)
                              .collect(Collectors.toList());
25
shmosel

On dirait que ce n'est pas actuellement en goyave, mais est une fonctionnalité souhaitée. Voir ce problème de github , en particulier Iterators.Zip().

16
Steve Kuo

Imaginez qu'il s'agit d'une méthode de goyave:

for (int i = 0; i < names.size(); i++) {
    persons.add(new Person(names.get(i), ages.get(i)));
}
9
stepanian

Vous pouvez vous référer à underscore-Java library .

Underscore-Java est un portage de Underscore.js pour Java et la méthode Zip peut atteindre cet objectif.

Voici un exemple de code et de sortie:

$.Zip(Arrays.asList("moe", "larry", "curly"), Arrays.asList("30", "40", "50"));

=> [[moe, 30], [larry, 40], [frisé, 50]]

2
sheldon shen

Voici un moyen générique de compresser des listes avec Vanilla Java. En l'absence de tuples, j'ai choisi d'utiliser une liste d'entrées de la carte (si vous n'aimez pas utiliser les entrées de la carte, ajoutez une classe ZipEntry supplémentaire ou quelque chose du genre).

public static <T1,T2> List<Map.Entry<T1,T2>> Zip(List<T1> zipLeft, List<T2> zipRight) {
    List<Map.Entry<T1,T2>> zipped = new ArrayList<>();
    for (int i = 0; i < zipLeft.size(); i++) {
        zipped.add(new AbstractMap.SimpleEntry<>(zipLeft.get(i), zipRight.get(i)));
    }
    return zipped;
}

Pour prendre également en charge les tableaux:

@SuppressWarnings("unchecked")
public static <T1,T2> Map.Entry<T1,T2>[] Zip(T1[] zipLeft, T2[] zipRight) {
    return Zip(asList(zipLeft), asList(zipRight)).toArray(new Map.Entry[] {});
}

Pour le rendre plus robuste, ajoutez des vérifications préalables sur les tailles de liste, etc., ou introduisez jointure gauche / jointure droite sémantique similaire aux requêtes SQL.

0
Benny Bottema

Voici une version sans itération explicite, mais elle devient plutôt moche.

List<Person> persons = ImmutableList.copyOf(Iterables.transform(
    ContiguousSet.create(Range.closedOpen(0, names.size()),
        DiscreteDomain.integers()),
    new Function<Integer, Person>() {
      @Override
      public Person(Integer index) {
        return new Person(names.get(index), ages.get(index));
      }
    }));

Ce n'est vraiment pas mieux que d'avoir une itération explicite, et vous voulez probablement un certain niveau de vérification des limites pour vous assurer que les deux entrées ont bien la même taille.

0
Zhe