リストの要素をグループ化したい。私は現在この方法でやっています:
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);
}
できればグアバを使用して、これを行うより良い方法はありますか?
確かにそれは可能ですが、グアバではさらに簡単です:) Multimaps.index(Iterable, Function)
:
_ImmutableListMultimap<E, E> indexed = Multimaps.index(list, groupFunction);
_
具体的なユースケースを指定すると、実際に使用するのが簡単になります。
ドキュメントの例:
_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);
_
プリント
_{4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
_
あなたの場合、GroupFunctionが次のように定義されている場合:
_GroupFunction<String> groupFunction = new GroupFunction<String>() {
@Override public String sameGroup(final String s1, final String s2) {
return s1.length().equals(s2.length());
}
}
_
次のように変換されます:
_Function<String, Integer> stringLengthFunction = new Function<String, Integer>() {
@Override public Integer apply(final String s) {
return s.length();
}
}
_
guavaの例で使用されているstringLengthFunction
実装が可能です。
最後に、Java 8で、ランバとメソッドの参照がインライン化するのに十分簡潔であるため、スニペット全体がさらに単純になる可能性があります。
_ImmutableListMultimap<E, E> indexed = Multimaps.index(list, String::length);
_
純粋なJava 8(グアバなし)の例 _Collector.groupingBy
_ を使用) Jeffrey Bosboom's answer を参照してください。ただし、違いはほとんどありませんアプローチ:
ImmutableListMultimap
ではなく、Map
の値を持つCollection
を返します。返されるマップのタイプ、可変性、直列化可能性、スレッドセーフ性についての保証はありません( source )、
[〜#〜] edit [〜#〜]:インデックスキーを気にしない場合は、グループ化された値を取得できます:
_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);
_
最初に望んでいたように、どのコンテンツをArrayList
に簡単にコピーできるか、またはそのまま使用できるかを_Lists<List<E>>
_ビューに表示します。また、indexed.get(key)
は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:Petr Gladkikhが 下のコメント に言及しているように、_Collection<List<E>>
_で十分な場合、上記の例はより簡単になります:
_Collection<List<E>> grouped = indexed.asMap().values();
_
Collector.groupingBy
from Java 8ストリームライブラリは、グアバのMultimaps.index
。 Xaerxessの答え の例を次に示します。Java 8ストリームを使用するように書き直されました。
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);
これは印刷されます
{4=[Inky], 5=[Pinky, Pinky, Clyde], 6=[Blinky]}
リストを作成する以外の方法で値を同じキーと組み合わせたい場合は、別のコレクターを取得するgroupingBy
のオーバーロードを使用できます。この例では、文字列を区切り文字で連結します。
Map<Integer, String> index = badGuys.stream()
.collect(Collectors.groupingBy(String::length, Collectors.joining(" and ")));
これは印刷されます
{4=Inky, 5=Pinky and Pinky and Clyde, 6=Blinky}
リストが大きい場合、またはグループ化関数が高価な場合は、parallelStream
と並行コレクターを使用して並列に実行できます。
Map<Integer, List<String>> index = badGuys.parallelStream()
.collect(Collectors.groupingByConcurrent(String::length));
これは印刷される場合があります(順序はもはや決定的ではありません)
{4=[Inky], 5=[Pinky, Clyde, Pinky], 6=[Blinky]}
最も簡単で簡単な方法は、次を使用することです: Lamdaj grouping feature
上記の例を書き換えることができます。
List<String> badGuys = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Group group = group(badGuys, by(on(String.class).length)));
System.out.println(group.keySet());
Java 8、グアバといくつかのヘルパー関数を使用すると、カスタムコンパレータでグループ化を実装できます
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);
}
例
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);
});