Java 将列表中的元素分组为子列表(可能使用guava)

Java 将列表中的元素分组为子列表(可能使用guava),java,list,function,grouping,guava,Java,List,Function,Grouping,Guava,我想对列表中的元素进行分组。我现在是这样做的: 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) {

我想对列表中的元素进行分组。我现在是这样做的:

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);
}
公共静态列表组(最终列表列表,最终组函数GroupFunction){
列表结果=Lists.newArrayList();
对于(最终E元素:列表){
布尔groupFound=false;
对于(最终列表组:结果){
if(groupFunction.sameGroup(元素,group.get(0))){
组。添加(元素);
groupFound=true;
打破
}
}
如果(!groupFound){
List newGroup=Lists.newArrayList();
添加(元素);
结果。添加(新组);
}
}
返回结果;
}
公共接口组函数{
公共布尔sameGroup(最终E元素1,最终E元素2);
}

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

当然有可能,用番石榴更容易:)使用:

在您的情况下,如果GroupFunction定义为:

GroupFunction<String> groupFunction = new GroupFunction<String>() {
  @Override public String sameGroup(final String s1, final String s2) {
    return s1.length().equals(s2.length());
  }
}
对于使用see的纯Java 8(无番石榴)示例,尽管该方法几乎没有区别:

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

  • 它比Guava+方法参考更详细

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

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);
编辑2:正如Petr Gladkikh提到的,如果
收集足够,上述示例可能更简单:

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

最简单的方法是使用:

上述示例可以重新编写:

List<String> badGuys = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Group group = group(badGuys, by(on(String.class).length)));
System.out.println(group.keySet());
List badGuys=Arrays.asList(“Inky”、“Blinky”、“Pinky”、“Pinky”、“Clyde”);
组=组(坏人,由(on(String.class.length));
System.out.println(group.keySet());
Java 8 streams库中的
提供了与Guava的
Multimaps.index
相同的功能。以下是中的示例,重写为使用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);
如果要以创建列表以外的其他方式将值与同一个键组合,可以使用另一个收集器的
groupingBy
重载。此示例使用分隔符连接字符串:

Map<Integer, String> index = badGuys.stream()
    .collect(Collectors.groupingBy(String::length, Collectors.joining(" and ")));
如果您有一个大的列表,或者您的分组函数很昂贵,那么您可以使用
parallelStream
和并发收集器进行并行处理

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

使用Java8、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);
}
公共静态映射组(列表项、比较器)
{
ListMultimap blocks=LinkedListMultimap.create();
如果(!ArrayUtils.isNullOrEmpty(项目))
{
T currentItem=null;
对于(T项:项)
{
如果(currentItem==null | |比较器。比较(currentItem,item)!=0)
{
当前项目=项目;
}
blocks.put(currentItem,ObjectUtils.clone(item));
}
}
返回Multimaps.asMap(块);
}
范例

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);
        });
Comparator Comparator=Comparator.comparingit(SportExercise::getEstimatedTime)
。然后比较它(SportExecute::getActiveTime)。然后比较它(SportExecute::getIntervalCount)
.Then比较长(SportExercise::getExerciseId);
图块=组(sportWorkout.GetTrainingExcessions(),比较程序);
blocks.forEach((键、值)->{
系统输出打印项次(键);
System.out.println(值);
});

这正是设计多重映射的目的。代替上一个代码示例
索引。asMap().values()
可能足以获得
集合
感谢这非常有用,例如,如何基于多个标准进行分组,假设您在函数中接收到一个包含两个字段的对象,并且您需要按此字段分组,我该如何做?java 7和8如何使用字符串作为番石榴分组的索引?@Alex78191您的确切意思是什么?似乎是一个单独的问题。对于多级分组,您可以按属性值列表进行分组。
List<String> badGuys = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Group group = group(badGuys, by(on(String.class).length)));
System.out.println(group.keySet());
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]}
Map<Integer, String> index = badGuys.stream()
    .collect(Collectors.groupingBy(String::length, Collectors.joining(" and ")));
{4=Inky, 5=Pinky and Pinky and Clyde, 6=Blinky}
Map<Integer, List<String>> index = badGuys.parallelStream()
    .collect(Collectors.groupingByConcurrent(String::length));
{4=[Inky], 5=[Pinky, Clyde, Pinky], 6=[Blinky]}
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);
        });