我不想使用Stream API进行排序并以整齐的方式创建列表。也许用Stream直接做到这一点是不可能的。 最终目标是对数据进行统计。但这不是问题所在。
源来自 ffprobe ,它是一个简单的框架信息列表:位置,类型(I或B / P)以及统计信息的其他信息。 解析没问题:我有一个很大的框架列表。
框架就像:
int position = 454
enum type = I
... // others datas for stats
好的,现在我不想通过GOP列表重新组合帧。 GOP,“图片组”是视频帧列表。它始终以帧类型“I”开始。 GOP可以像IPBBPBBPBBP一样。大小(帧的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 loop 来实现它并不太复杂。
但是,有了Stream和它的朋友(收藏家),我怎么能这样做?!这可以比老式的 更快吗?
感谢阅读并度过了美好的一天。
PS:这是一个开源应用程序。
编辑:经过简单的测试,for循环保持非常快达155000帧:在一个简单的i7核心上,74毫秒用于完成工作(并且花费更少的时间来打开数据文件)。 所以...似乎不需要流API。 但现在,尤金的回答非常有启发性。谢谢Eugene!
答案 0 :(得分:2)
我已经简化了你的输入以进行测试。所以这就是我想象的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());
结果将是:
[[pos = 0 type = I, pos = 1 type = G, pos = 2 type = B],
[pos = 3 type = I, pos = 4 type = B]]
indexes
将捕获索引,其中“I”也附加列表的大小。而不是一个简单的子列表来获得所需的列表。
现在这个不会像for循环一样快,所以这里没有性能提升。通常,流比通常的for循环慢。使用parallel
流时,您可以获得唯一的性能提升 - 但您需要列表中批次的元素。
答案 1 :(得分:1)
不要混淆另一个答案,我想(我发誓我已经在某个地方看到了这个!)显示另一个自定义收藏家。
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;
}));
我将尝试用视觉(对于并行流)解释这里发生的事情。假设输入为:
IPBIHGIR
这就是并行收集器的用途。
[() (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
只修剪第一个空数组。
我再说一遍再次:我已经在某个地方看到了这个(当时需要一段时间才能理解),因此绘图 - 可能会帮助其他人。
如果您阅读本文并了解原始来源,请发表评论,我很乐意将其写入答案,以便人们了解这一点的真正创造者......
答案 2 :(得分:0)
经过简单的测试,for循环可以快速达到155000帧:在一个简单的i7核心上,74毫秒用于完成工作(并且花费更少的时间来打开数据文件)。所以...似乎不需要流API。但现在,尤金的回答非常有启发性。谢谢尤金!