将列表的元素分组为子列表(也许通过使用番石榴)

发布于 2024-12-20 09:52:36 字数 892 浏览 2 评论 0 原文

我想对列表的元素进行分组。我目前正在这样做:

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

有没有更好的方法来做到这一点,最好是使用番石榴?

I want to group elements of a list. I'm currently doing it this way:

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

Is there a better way to do this, preferably by using guava?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

a√萤火虫的光℡ 2024-12-27 09:52:36

当然这是可能的,而且使用 Guava 更容易:) 使用 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);

prints

{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 中,整个代码片段可能会更简单,因为ambas和方法引用足够简洁,可以内联:

ImmutableListMultimap<E, E> indexed = Multimaps.index(list, String::length);

对于纯 Java 8(无 Guava)示例,使用 Collector.groupingBy 参见<一href="https://stackoverflow.com/a/25953796/708434">Jeffrey Bosboom 的答案,尽管该方法有一些差异:

  • 它不返回 ImmutableListMultimap 而是返回带有 Collection 值的 Map
  • 对返回的 Map 的类型、可变性、可序列化性或线程安全性没有任何保证 (),

  • 它比番石榴+方法参考。

编辑:如果您不关心索引键,您可以获取分组值:

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

什么让您Lists>查看可以轻松复制到哪些内容ArrayList 或按原样使用,正如您首先想要的那样。另请注意,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());

编辑2:正如Petr Gladkikh提到的在下面的评论中,如果Collection> 就足够了,上面的例子可以更简单:

Collection<List<E>> grouped = indexed.asMap().values();

Sure it is possible, and even easier with Guava :) Use Multimaps.index(Iterable, Function):

ImmutableListMultimap<E, E> indexed = Multimaps.index(list, groupFunction);

If you give concrete use case it would be easier to show it in action.

Example from 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);

prints

{4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}

In your case if GroupFunction is defined as:

GroupFunction<String> groupFunction = new GroupFunction<String>() {
  @Override public String sameGroup(final String s1, final String s2) {
    return s1.length().equals(s2.length());
  }
}

then it would translate to:

Function<String, Integer> stringLengthFunction = new Function<String, Integer>() {
  @Override public Integer apply(final String s) {
    return s.length();
  }
}

which is possible stringLengthFunction implementation used in Guava's example.


Finally, in Java 8, whole snippet could be even simpler, as lambas and method references are concise enough to be inlined:

ImmutableListMultimap<E, E> indexed = Multimaps.index(list, String::length);

For pure Java 8 (no Guava) example using Collector.groupingBy see Jeffrey Bosboom's answer, although there are few differences in that approach:

  • it doesn't return ImmutableListMultimap but rather Map with Collection values,
  • There are no guarantees on the type, mutability, serializability, or thread-safety of the Map returned (source),

  • it's a bit more verbose than Guava + method reference.

EDIT: If you don't care about indexed keys you can fetch grouped values:

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

what gives you Lists<List<E>> view which contents can be easily copied to ArrayList or just used as is, as you wanted in first place. Also note that indexed.get(key) is 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 mentions in comment below, if Collection<List<E>> is enough, above example could be simpler:

Collection<List<E>> grouped = indexed.asMap().values();
岁吢 2024-12-27 09:52:36

Java 8 流库中的 Collector.groupingBy 提供与 Guava 的 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 ")));

This will print

{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]}

Collector.groupingBy from the Java 8 streams library provides the same functionality as Guava's Multimaps.index. Here's the example in Xaerxess's answer, rewritten to use 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);

This will print

{4=[Inky], 5=[Pinky, Pinky, Clyde], 6=[Blinky]}

If you want to combine the values with the same key in some other way than creating a list, you can use the overload of groupingBy that takes another collector. This example concatenates the strings with a delimiter:

Map<Integer, String> index = badGuys.stream()
    .collect(Collectors.groupingBy(String::length, Collectors.joining(" and ")));

This will print

{4=Inky, 5=Pinky and Pinky and Clyde, 6=Blinky}

If you have a large list or your grouping function is expensive, you can go parallel using parallelStream and a concurrent collector.

Map<Integer, List<String>> index = badGuys.parallelStream()
    .collect(Collectors.groupingByConcurrent(String::length));

This may print (the order is no longer deterministic)

{4=[Inky], 5=[Pinky, Clyde, Pinky], 6=[Blinky]}
柒七 2024-12-27 09:52:36

最简单的方法是使用: Lambdaj 分组功能

上面的示例可以被重写:

List<String> badGuys = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Group group = group(badGuys, by(on(String.class).length)));
System.out.println(group.keySet());

The easiest and simplest way would be using: Lambdaj grouping feature

The above example can be re-written:

List<String> badGuys = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Group group = group(badGuys, by(on(String.class).length)));
System.out.println(group.keySet());
深爱成瘾 2024-12-27 09:52:36

使用 Java 8、Guava 和一些辅助函数,您可以使用自定义比较器

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

With Java 8, Guava and few helper functions you can implement grouping with custom Comparator

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

Example

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);
        });
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文