【发布时间】:2017-01-28 10:15:15
【问题描述】:
延迟过滤 CSV 文件
我需要过滤存储为大量 CSV 文件的数百万条日志记录。记录的大小大大超出了我的可用内存,所以我想采用一种懒惰的方法。
Java 8 流 API
使用jdk8,我们拥有与Apache commons-csv 配对的Streams API,让我们可以轻松完成此任务。
public class LazyFilterer {
private static Iterable<CSVRecord> getIterable(String fileName) throws IOException {
return CSVFormat
.DEFAULT
.withFirstRecordAsHeader()
.parse(new BufferedReader(new FileReader(fileName)));
}
public static void main(String[] args) throws Exception {
File dir = new File("csv");
for (File file : dir.listFiles()) {
Iterable<CSVRecord> iterable = getIterable(file.getAbsolutePath());
StreamSupport.stream(iterable.spliterator(), true)
.filter(c -> c.get("API_Call").equals("Updates"))
.filter(c -> c.get("Remove").isEmpty())
.forEach(System.out::println);
}
}
}
性能
这张来自 VisualVM 的图表显示了使用比上图更复杂的过滤管道1 解析 2.3 GB CSV 文件期间的内存使用情况。
如您所见,内存使用量在过滤发生时基本保持不变2。
你能找到另一种方法来更快地完成相同的任务,同时不增加代码复杂性吗?
欢迎任何语言,Java 不一定是首选!
脚注
[1] - 例如对于与"API_Call" 匹配的每个CSVRecord,我可能需要进行一些JSON 反序列化并在此之后进行额外的过滤,或者甚至为某些记录创建一个对象以促进额外的计算。
[2] - 图表开头的空闲时间是 System.in.read(),用于确保 VisualVM 在计算开始之前已完全加载。
【问题讨论】:
-
你在自相矛盾。当您同时说“记录的大小大大超出了我的可用内存”时,“将整个 CSV 文件读取到内存的简单算法”再快不过了。
-
没错,好点。您可以使用“如果我有足够的可用内存”或“用于一小部分数据”的条件来接受该陈述
-
我看不出将假设场景的性能与真实场景的性能进行比较有什么意义。此外,您没有命名“幼稚的实现”,此外,您没有显示任何有关性能的数字。因此,您的问题是基于一个空洞的说法,即未指定的实现将比您在不适用的假设场景中所做的更快。
-
@Holger 我删除了这句话,因为它与问题无关。如果您想提供答案并且需要确定您的解决方案与我给出的解决方案相比的性能,您可以生成一些 CSV 文件并在您自己的本地计算机上运行它们。很遗憾,我无法提供我实际过滤的任何 CSV 文件。
-
这与“问题无关”。完全不清楚为什么您认为必须有比您已有的解决方案更快的解决方案。以及仅要求工具或库或 SO 题外话的问题。
标签: csv functional-programming java-8 java-stream lazy-evaluation