Java 8 Java8:按值聚合项目列表并按位置范围重新分组
我不想使用流API来有序地排序和创建列表。也许直接用流来实现这一点是不可能的。 最终目标是对数据进行统计。但这不是问题所在 源代码来自ffprobe,它是一个简单的帧信息列表:位置、类型(I或B/p)和其他统计信息。 解析还可以:我有一个大的帧列表 框架类似于: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”开始。共和党可
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)]