Java: 提高解析大文件的速度
Java: increasing speed of parsing large file
我有 csv 文件:
让我们称之为 product.csv
Product_group | Product producer | Product_name | CODE | RANDOM_F_1 | ... | RANDOM_F_25
----------------------------------------------------------------------------------------
Electronic | Samsung | MacBook_1 | 60 | 0.8 | ... | 1.2
Electronic | Samsung | MacBook_2 | | 0.8 | ... | 1.2
... | ... | ... | | ... | ... | ...
Electronic | Samsung | MacBook_9999 | 63 | 1.2 | ... | 3.1
Electronic | Samsung | MacBook_9999 | 64 | 1.2 | ... | 3.1
我将尝试解释这个 csv 文件:
Product_name + CODE 对是唯一的(如果存在代码),RANDOM_F_1 是具有随机值的字段。
所以,我的目标:
我有 java class,它生成这个 csv 文件。当它生成新文件时 - 它将清理 product.csv,并生成具有 其他随机属性 .
的新文件
现在,我有一个目标 - 不要在新的 csv 生成中覆盖这个随机字段。
所以,我有一个想法 - 在清理之前创建此 csv 文件的副本,如果副本文件中存在 MacBook_9999 - 只需在新一代中使用此原始文件文件。
我的代码现在看起来像:
public void createProducts(List<Products> products) {
//copying file
for(Product newProduct : products) {
Product previousProduct = findPreviousProduct(newProduct);
if(previousProduct != null) {
newProduct = previousProduct;
}
addToCsv(newProduct );
}
}
private void copyFile() {
//here i am copying file by FileInputStream and FileOutputStream
}
private Product findPreviousProduct(Product product) {
File copyFile = new File(...);
//creation BufferReader br here, in try with resources
previousProduct = br.lines().parallel()
.map(Product::new)
.filter(e -> e.getName.equals(product.getName) && //here is comparison by code)
.findFirst().orElse(...);
//return statement here
}
Everythink 工作正常,但在添加此检查后我遇到了一个性能问题,请参见下面的测试示例(包含 12k 原始文件的文件):
之前:3秒
之后:2 分钟 20 秒
所以,问题是:我如何提升它?我应该使用其他方式来保存我的 RANDOM 字段吗?
因为性能真的很差。如果我有 100k 原始文件,则需要 22 分钟:(
在散列映射中保存数据(Blaž Mrak 评论)并通过键获取行的想法很简单,但如果我将有 500-700k 个对象 - 我的堆内存将结束。
开发者,比起你们
我认为你的复杂度不是 O(n),而是 O(n^2),这意味着对于 100k 行,你的代码将 运行 持续 220 分钟,而不是 22 分钟。是什么让更糟糕的是,您每次调用 findPreviousProduct 时都在读取文件。我建议首先将 csv 加载到内存中,然后搜索它:
//somewhere else... MyCsvReader or sth
public List<Product> readPerviousProducts() {
File copyFile = new File(...);
...
return br.lines().parallel()
.map(Product::new).toList();
}
//then in your class
public void createProducts(List<Product> products, List<Product> previousProducts) {
for(Product newProduct : products) {
Product previousProduct = findPreviousProduct(previousProducts, newProduct);
if(previousProduct != null) {
newProduct = previousProduct;
}
addToCsv(newProduct );
}
}
private Product findPreviousProduct(List<Product> previousProducts, Product product) {
return previousProducts.filter(e -> e.getName.equals(product.getName) && //here is comparison by code)
.findFirst().orElse(...);
}
先试试这个,看看是否有一些性能改进。第二个优化是创建一个 HashMap 而不是一个 List。您在产品上创建一个 key() 方法,它将 return 从名称和代码生成一个唯一的字符串。 (基本上就是 name + _ + code)
//somewhere else
public List<Product> readPerviousProducts() {
File copyFile = new File(...);
...
return br.lines().parallel()
.map(Product::new)
.toMap((product) -> product.key(), (product) -> product);
}
public void createProducts(List<Product> products, HashMap<String, Product> previousProducts) {
for(Product newProduct : products) {
Product previousProduct = findPreviousProduct(previousProducts, newProduct);
if(previousProduct != null) {
newProduct = previousProduct;
}
addToCsv(newProduct );
}
}
private Product findPreviousProduct(List<Product> previousProducts, Product product) {
return previousProducts.get(product.key());
}
然后您可以比较每个解决方案的速度有多快:)
我有 csv 文件: 让我们称之为 product.csv
Product_group | Product producer | Product_name | CODE | RANDOM_F_1 | ... | RANDOM_F_25
----------------------------------------------------------------------------------------
Electronic | Samsung | MacBook_1 | 60 | 0.8 | ... | 1.2
Electronic | Samsung | MacBook_2 | | 0.8 | ... | 1.2
... | ... | ... | | ... | ... | ...
Electronic | Samsung | MacBook_9999 | 63 | 1.2 | ... | 3.1
Electronic | Samsung | MacBook_9999 | 64 | 1.2 | ... | 3.1
我将尝试解释这个 csv 文件: Product_name + CODE 对是唯一的(如果存在代码),RANDOM_F_1 是具有随机值的字段。
所以,我的目标:
我有 java class,它生成这个 csv 文件。当它生成新文件时 - 它将清理 product.csv,并生成具有 其他随机属性 .
的新文件现在,我有一个目标 - 不要在新的 csv 生成中覆盖这个随机字段。
所以,我有一个想法 - 在清理之前创建此 csv 文件的副本,如果副本文件中存在 MacBook_9999 - 只需在新一代中使用此原始文件文件。
我的代码现在看起来像:
public void createProducts(List<Products> products) {
//copying file
for(Product newProduct : products) {
Product previousProduct = findPreviousProduct(newProduct);
if(previousProduct != null) {
newProduct = previousProduct;
}
addToCsv(newProduct );
}
}
private void copyFile() {
//here i am copying file by FileInputStream and FileOutputStream
}
private Product findPreviousProduct(Product product) {
File copyFile = new File(...);
//creation BufferReader br here, in try with resources
previousProduct = br.lines().parallel()
.map(Product::new)
.filter(e -> e.getName.equals(product.getName) && //here is comparison by code)
.findFirst().orElse(...);
//return statement here
}
Everythink 工作正常,但在添加此检查后我遇到了一个性能问题,请参见下面的测试示例(包含 12k 原始文件的文件):
之前:3秒
之后:2 分钟 20 秒
所以,问题是:我如何提升它?我应该使用其他方式来保存我的 RANDOM 字段吗?
因为性能真的很差。如果我有 100k 原始文件,则需要 22 分钟:(
在散列映射中保存数据(Blaž Mrak 评论)并通过键获取行的想法很简单,但如果我将有 500-700k 个对象 - 我的堆内存将结束。
开发者,比起你们
我认为你的复杂度不是 O(n),而是 O(n^2),这意味着对于 100k 行,你的代码将 运行 持续 220 分钟,而不是 22 分钟。是什么让更糟糕的是,您每次调用 findPreviousProduct 时都在读取文件。我建议首先将 csv 加载到内存中,然后搜索它:
//somewhere else... MyCsvReader or sth
public List<Product> readPerviousProducts() {
File copyFile = new File(...);
...
return br.lines().parallel()
.map(Product::new).toList();
}
//then in your class
public void createProducts(List<Product> products, List<Product> previousProducts) {
for(Product newProduct : products) {
Product previousProduct = findPreviousProduct(previousProducts, newProduct);
if(previousProduct != null) {
newProduct = previousProduct;
}
addToCsv(newProduct );
}
}
private Product findPreviousProduct(List<Product> previousProducts, Product product) {
return previousProducts.filter(e -> e.getName.equals(product.getName) && //here is comparison by code)
.findFirst().orElse(...);
}
先试试这个,看看是否有一些性能改进。第二个优化是创建一个 HashMap 而不是一个 List。您在产品上创建一个 key() 方法,它将 return 从名称和代码生成一个唯一的字符串。 (基本上就是 name + _ + code)
//somewhere else
public List<Product> readPerviousProducts() {
File copyFile = new File(...);
...
return br.lines().parallel()
.map(Product::new)
.toMap((product) -> product.key(), (product) -> product);
}
public void createProducts(List<Product> products, HashMap<String, Product> previousProducts) {
for(Product newProduct : products) {
Product previousProduct = findPreviousProduct(previousProducts, newProduct);
if(previousProduct != null) {
newProduct = previousProduct;
}
addToCsv(newProduct );
}
}
private Product findPreviousProduct(List<Product> previousProducts, Product product) {
return previousProducts.get(product.key());
}
然后您可以比较每个解决方案的速度有多快:)