Java 8 Java8:按值聚合项目列表并按位置范围重新分组

Java 8 Java8:按值聚合项目列表并按位置范围重新分组,java-8,java-stream,Java 8,Java Stream,我不想使用流API来有序地排序和创建列表。也许直接用流来实现这一点是不可能的。 最终目标是对数据进行统计。但这不是问题所在 源代码来自ffprobe,它是一个简单的帧信息列表:位置、类型(I或B/p)和其他统计信息。 解析还可以:我有一个大的帧列表 框架类似于: int position = 454 enum type = I ... // others datas for stats 好的,现在我不想按GOP列表重新组合帧。GOP“图片组”是一个视频帧列表。它总是以帧类型“I”开始。共和党可

我不想使用流API来有序地排序和创建列表。也许直接用流来实现这一点是不可能的。 最终目标是对数据进行统计。但这不是问题所在

源代码来自ffprobe,它是一个简单的帧信息列表:位置、类型(I或B/p)和其他统计信息。 解析还可以:我有一个大的帧列表

框架类似于:

int position = 454
enum type = I
... // others datas for stats
好的,现在我不想按GOP列表重新组合帧。GOP“图片组”是一个视频帧列表。它总是以帧类型“I”开始。共和党可以像IPBBPBBBP一样。视频流中的大小(帧的nb)和顺序可以改变。我无法预测

如何恢复GOP列表

1) I start my first GOP with the first frame (position = 0, type = I).
2) for each next frames, I check the type. If type = B or P, I add this frame to my current GOP.
3) but if this frame type is a "I", I "close" the current GOP list, create a new GOP list, and I add this frame. Goto 2) as long as there are frames.
好的,用for循环实现这个并不太复杂

但是,有了Stream和它的朋友(收藏家),我怎么能做到这一点?!这件衣服能比旧衣服穿得更快吗

谢谢你的阅读,祝你度过愉快的一天

PS:这是一款开源应用程序

编辑:在一个简单的测试之后,for loop的速度非常快,最高可达155000帧:在一个简单的i7内核上,完成这项工作需要74毫秒(并且只需花费更少的时间来打开数据文件)。 所以流API似乎不是必需的。
但现在,尤金的答案很有启发性。谢谢你,尤金

我已经简化了您的输入以进行测试。下面是我想象的
Frame

static class Frame {

    private final int position;

    private final String type;

    public Frame(int position, String type) {
        super();
        this.position = position;
        this.type = type;
    }

    public int getPosition() {
        return position;
    }

    public String getType() {
        return type;
    }

    @Override
    public String toString() {
        return "pos = " + position + " type = " + type;
    }

}
下面是我解决问题的方法:

List<Frame> list = 
         Arrays.asList(
              new Frame(0, "I"),
              new Frame(1, "G"), 
              new Frame(2, "B"), 
              new Frame(3, "I"), 
              new Frame(4, "B"));

    int[] indexes = IntStream.concat(IntStream.range(0, list.size())
            .filter(i -> list.get(i).getType().equals("I")),  
                    IntStream.of(list.size()))
            .toArray();

    List<List<Frame>> frames = IntStream.range(0, indexes.length - 1)
            .mapToObj(x -> list.subList(indexes[x], indexes[x + 1]))
            .collect(Collectors.toList());
索引
将捕获在列表大小后面附加“I”的索引。而不是一个简单的子列表来获得所需的列表


现在这个将不会像for循环那样快,所以这里没有性能提升。一般来说,对于环路,流的速度比通常的慢。当使用
并行
流时,您可以获得的唯一性能增益-但是您需要列表中的许多元素。

为了不让其他答案变得混乱,我想(我发誓我已经在某处看到了!)显示另一个自定义收集器

List<List<Frame>> custom2 = list.stream()
            .collect(Collector.of(
                    () -> {
                        List<List<Frame>> supList = new ArrayList<>();
                        supList.add(new ArrayList<>());
                        return supList;
                    },
                    (l, frame) -> {
                        if (frame.getType().equals("I")) {
                            l.add(new ArrayList<>());   
                        }
                        l.get(l.size() - 1).add(frame);
                    },
                    (left, right) -> {
                        List<Frame> first = right.remove(0);
                        left.get(left.size() - 1).addAll(first);
                        left.addAll(right);
                        return left;
                    },
                    result -> {
                        result.remove(0);
                        return result;
                    }));
[() (I)]   [(P)]   [(B)]  [() (I)]   [(H)]   [(G)]   [() (I)]   [(R)]  
  \         /        \       /         \       /        \         / 
   \       /          \     /           \     /          \       /
  [() (I, P)]        [(B) (I)]          [(H, G)]        [() (I, R)]
       \                 /                  \               /                
        \               /                    \             /
         \             /                      \           /
          \           /                        \         /
         [() (I,P,B) (I)]                     [(H,G) (I, R)]
               \                                    /
                \                                  /  
                 \                                /
                  \                              /
                   \                            /
                    \                          /
                    [(), (I,P,B) (I,H,G), (I,R)]
finisher
只修剪第一个空数组

我将再次重复这一点:我已经在某处看到了这一点(当时理解这一点需要一段时间),因此这幅画可能会帮助其他人


如果你读了这篇文章并知道了原始来源,请留下评论,我会很高兴地把它写进答案中,这样人们就能知道这篇文章的真正作者……

经过一个简单的测试,for loop的速度可以达到155000帧:在一个简单的i7内核上,完成这项工作需要74毫秒(并且只需花费更少的时间打开数据文件)。所以流API似乎不是必需的。但现在,尤金的答案很有启发性。谢谢你,尤金

在我看来,溪流不太适合这种工作。我看到了,如果你满意的话,请不要让我从中减去。流最适合独立地处理元素,可以根据每个元素的某些固有属性进行分组,但不能根据最近的前一个
I
元素进行分组。我倾向于选择这里的循环解决方案。一般来说,我也会进行循环,除非有不这样做的理由。我确实试图提出(在第二个答案中)这样一个理由——可以实现一个定制收集器,它可以并行处理大量条目;对循环来说不是那么容易…哇。。。这真的很聪明。。。一个视频文件可以有很多帧。以每秒60帧的速度拍摄2小时电影。。。Java将接受face 2*3600*60=432.000帧对象…这是JVM要管理的很多项。。。在这里等待响应时,我实现了一个for循环,它看起来相当快。你认为流API会更快、更少地消耗内存吗?@hdsdi3g当性能很重要时,我首先衡量,然后再考虑。流肯定会使用
更多的内存
,就速度而言-您无法轻松地并行化for循环。。。所以衡量一下——我真的说不出来。如果您有足够的数据(听起来像是这样),并行流的执行速度可能会更快。嗨,尤金,虽然我认为这是可行的,但我不明白为什么在开始时添加一个额外的“行”并在结束时删除它对并行流更好。@FedericoPeraltaSchaffner这花了很长时间才弄清楚(因此,两个答案之间的时间差距很大)我不太确定我是否可以在不画太多的情况下正确解释这一点。简言之,它就像一个标记,将这一行与下一行分开。假设这个输入
[()(I)][(P)][(B)][()(I)][(H)]
,结果我们需要累积这两个:
[()(I)]
[(H)]
[() (I)]   [(P)]   [(B)]  [() (I)]   [(H)]   [(G)]   [() (I)]   [(R)]  
  \         /        \       /         \       /        \         / 
   \       /          \     /           \     /          \       /
  [() (I, P)]        [(B) (I)]          [(H, G)]        [() (I, R)]
       \                 /                  \               /                
        \               /                    \             /
         \             /                      \           /
          \           /                        \         /
         [() (I,P,B) (I)]                     [(H,G) (I, R)]
               \                                    /
                \                                  /  
                 \                                /
                  \                              /
                   \                            /
                    \                          /
                    [(), (I,P,B) (I,H,G), (I,R)]