我有一个大约10.000.000行文本的文件(是的,我有足够的内存)。
现在我想要一个MyClass
的列表(构造函数是MyClass(String s)
与文件的每一行。现在我这样做:
List<MyClass> help = Files.lines(Paths.get(s))
.parallel()
.map(MyClass::new)
.collect(Collectors.toList());
但需要数年才能取得进展。关于如何加速这个问题的任何想法?
答案 0 :(得分:1)
首先,来自Collectors.toList()
的文档的相关摘录:
[...]无法保证返回的List的类型,可变性,可序列性或线程安全性;如果需要更多地控制返回的List,请使用toCollection(Supplier)
现在,让我们更深入地了解收藏家的characteristics;我们发现了这个:
public static final Collector.Characteristics CONCURRENT
表示此收集器是并发的,这意味着结果容器可以支持从多个线程同时使用相同结果容器调用累加器函数。
如果CONCURRENT收集器也不是UNORDERED,那么只有在应用于无序数据源时才应同时评估它。
现在,没有任何保证Collectors.toList()
返回的收集器完全是Concurrent
。
尽管有时间启动你的新类,但这里的安全措施是假设这个收集器不是并发。但幸运的是,我们有一种方法可以使用并发集合,如javadoc中所述。那么,让我们试试:
.collect(
Collector.of(CopyOnWriteArrayList::new,
List::add,
(o, o2) -> { o.addAll(o2); return o; },
Function.<List<String>>identity(),
Collector.Characteristics.CONCURRENT,
Collector.Characteristics.IDENTITY_FINISH
)
)
这可能会加快速度。
现在,您还有另一个问题。你没有关闭你的流。
这个鲜为人知,但是Stream
(无论是任何类型,还是{Int,Double,Long}流)都实现了AutoCloseable
。您想要关闭I / O绑定的流,Files.lines()
就是这样的流。
所以,试试这个:
final List<MyClass> list;
try (
final Stream<String> lines = Files.lines(...);
) {
list = lines.parallel().map(MyClass::new)
.collect(seeAbove);
}