Regrouper des éléments d'une liste en sous-listes (peut-être en utilisant la goyave)
je veux regrouper des éléments d'une liste. Je le fais actuellement de cette façon:
public static <E> List<List<E>> group(final List<E> list, final GroupFunction<E> groupFunction) {
List<List<E>> result = Lists.newArrayList();
for (final E element : list) {
boolean groupFound = false;
for (final List<E> group : result) {
if (groupFunction.sameGroup(element, group.get(0))) {
group.add(element);
groupFound = true;
break;
}
}
if (! groupFound) {
List<E> newGroup = Lists.newArrayList();
newGroup.add(element);
result.add(newGroup);
}
}
return result;
}
public interface GroupFunction<E> {
public boolean sameGroup(final E element1, final E element2);
}
y a-t-il une meilleure façon de le faire, de préférence en utilisant la goyave?
4 réponses
bien sûr, il est possible, et encore plus facile avec la goyave :) utiliser Multimaps.index(Iterable, Function)
:
ImmutableListMultimap<E, E> indexed = Multimaps.index(list, groupFunction);
Si vous donnez cas concret d'utilisation, il serait plus facile de le montrer en action.
exemple tiré de docs:
List<String> badGuys =
Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Function<String, Integer> stringLengthFunction = ...;
Multimap<Integer, String> index =
Multimaps.index(badGuys, stringLengthFunction);
System.out.println(index);
imprime
{4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
dans votre cas si GroupFunction est défini comme:
GroupFunction<String> groupFunction = new GroupFunction<String>() {
@Override public String sameGroup(final String s1, final String s2) {
return s1.length().equals(s2.length());
}
}
alors il traduirait à:
Function<String, Integer> stringLengthFunction = new Function<String, Integer>() {
@Override public Integer apply(final String s) {
return s.length();
}
}
ce qui est possible stringLengthFunction
implémentation utilisée dans L'exemple de Guava.
enfin, en Java 8, tout l'extrait pourrait être encore plus simple, comme lambas et les références de méthode sont assez concises pour être inligné:
ImmutableListMultimap<E, E> indexed = Multimaps.index(list, String::length);
Par pure Java 8 (pas de Goyave) exemple d'utilisation de Collector.groupingBy
voir Jeffrey Bosboom la réponse de , bien qu'il n'y peu de différences dans cette approche:
- il n'a pas de retour
ImmutableListMultimap
mais plutôtMap
avecCollection
valeurs", -
Il n'y a pas de garanties sur le type, la mutabilité, serializability, ou le fil de sécurité de la Carte retournée ( source ),
- c'est un peu plus verbeux que la méthode Goyave + référence.
modifier : si vous ne vous souciez pas des clés indexées, vous pouvez récupérer des valeurs groupées:
List<List<E>> grouped = Lists.transform(indexed.keySet().asList(), new Function<E, List<E>>() {
@Override public List<E> apply(E key) {
return indexed.get(key);
}
});
// or the same view, but with Java 8 lambdas:
List<List<E>> grouped = Lists.transform(indexed.keySet().asList(), indexed::get);
ce que vous offre Lists<List<E>>
afficher le contenu peut être facilement copié ArrayList
ou tout simplement utilisé comme il est, comme vous le souhaitiez, en premier lieu. Notez également que indexed.get(key)
est ImmutableList
.
// bonus: similar as above, but not a view, instead collecting to list using streams:
List<List<E>> grouped = indexed.keySet().stream()
.map(indexed::get)
.collect(Collectors.toList());
EDIT 2 : As Petr Gladkikh mentionne dans le commentaire ci-dessous , si Collection<List<E>>
est suffisant, l'exemple ci-dessus pourrait être plus simple:
Collection<List<E>> grouped = indexed.asMap().values();
Collector.groupingBy
de la bibliothèque Java 8 streams fournit la même fonctionnalité que Multimaps.index
de Guava . Voici l'exemple dans réponse de Xaerxess , réécrit pour utiliser Java 8 streams:
List<String> badGuys = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Map<Integer, List<String>> index = badGuys.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(index);
ce sera imprimé
{4=[Inky], 5=[Pinky, Pinky, Clyde], 6=[Blinky]}
Si vous souhaitez combiner les valeurs avec la même clé d'une autre façon que de créer une liste, vous pouvez utiliser la surcharge de groupingBy
qui prend un autre collectionneur. Cet exemple concaténe les chaînes avec un délimiteur:
Map<Integer, String> index = badGuys.stream()
.collect(Collectors.groupingBy(String::length, Collectors.joining(" and ")));
ce sera imprimé
{4=Inky, 5=Pinky and Pinky and Clyde, 6=Blinky}
si vous avez une grande liste ou si votre fonction de regroupement est chère, vous pouvez aller en parallèle en utilisant parallelStream
et un collecteur concurrent.
Map<Integer, List<String>> index = badGuys.parallelStream()
.collect(Collectors.groupingByConcurrent(String::length));
Cela peut imprimer (l'ordre n'est plus déterministe)
{4=[Inky], 5=[Pinky, Clyde, Pinky], 6=[Blinky]}
la façon la plus simple et la plus simple serait d'utiliser: caractéristique de groupement Lamdaj
l'exemple ci-dessus peut être réécrit:
List<String> badGuys = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Group group = group(badGuys, by(on(String.class).length)));
System.out.println(group.keySet());
avec Java 8, Guava et peu de fonctions d'aide, vous pouvez mettre en œuvre le groupement avec un comparateur personnalisé
public static <T> Map<T, List<T>> group(List<T> items, Comparator<T> comparator)
{
ListMultimap<T, T> blocks = LinkedListMultimap.create();
if (!ArrayUtils.isNullOrEmpty(items))
{
T currentItem = null;
for (T item : items)
{
if (currentItem == null || comparator.compare(currentItem, item) != 0)
{
currentItem = item;
}
blocks.put(currentItem, ObjectUtils.clone(item));
}
}
return Multimaps.asMap(blocks);
}
exemple
Comparator<SportExercise> comparator = Comparator.comparingInt(SportExercise::getEstimatedTime)
.thenComparingInt(SportExercise::getActiveTime).thenComparingInt(SportExercise::getIntervalCount)
.thenComparingLong(SportExercise::getExerciseId);
Map<SportExercise, List<SportExercise>> blocks = group(sportWorkout.getTrainingExercises(), comparator);
blocks.forEach((key, values) -> {
System.out.println(key);
System.out.println(values);
});