Java Stream - 使用 Stream 并行读取 POI 表

问题描述 投票:0回答:1
我正在从输入文件中读取 POI 表,其中包含 80-400 个条目,第一个单元格包含整数代码:

| CODE | | 112 | | 112 | | 113 | | 114 | ...
我正在尝试计算每个

CODE

出现的次数。为此,我使用迭代器逐行迭代,当迭代器发现不存在的 
HashMap
 时使用 
CODE
 进行输入,或者如果 HashMap 中存在 
CODE
 则增加计数器:

var sheet=WorkbookFactory.create(new File("file.xlsx")).getSheetAt(0); var codeMap=new HashMap<String, Integer>(); var iterator=sheet.iterator(); iterator.next(); while(iterator.hasNext() { var cell=iterator.next().getCell(0); if(cell!=null) { var code=new DecimalFormat("00").format(cell.getNumericCellValue()); codeMap.computeIfPresent(code,(key,val)->val+1); codeMap.putIfAbsent(code,1); } } codeMap.forEach((key,value)->System.out.println("Code: "+key+", count: "+value));
我正在尝试将上面的代码转换为

parallelStream

以加快进程,我很难理解它。我确信这是可以做到的,但我不知道如何做到。

到目前为止我只知道创建一个流:

var stream=Stream.of(sheet).parallel(); HashMap<String,Integer> codeMap=stream.filter(); //map? filter? stuck here
我尝试阅读

this问题,但不明白发生了什么。

java java-stream apache-poi
1个回答
1
投票
要通过

java.util.stream.Stream

 使用 Apache POI 读取 Excel 工作表,
Sheet.spliterator 可用于使用 Stream<Row>
 创建 
StreamSupport

然后使用

Stream.map

可以得到一个包含一列所有内容的
Stream<String>
。为此,
Function
中的
Stream.map
需要获取每行该列的所有内容作为
String
。要获取单元格内容,应使用 
String
 
DataFormatter 来独立于不同的单元格类型。

人们可以使用具有功能标识和计数收集器的分组收集器来收集

Stream<String>

。结果是 
Map<String, Long>

完整代码示例:

import org.apache.poi.ss.usermodel.*; import java.io.FileInputStream; import java.util.Map; import java.util.Spliterator; import java.util.stream.Stream; import java.util.stream.StreamSupport; import java.util.stream.Collectors; import java.util.function.Function; public class ReadExcelUsingStreamsCountOccurence { public static void main(String[] args) { DataFormatter dataFormatter = new DataFormatter(); // from 5.2.0 on the DataFormatter can set to use cached values for formula cells, so no formula evaluation needed dataFormatter.setUseCachedValuesForFormulaCells(true); try (FileInputStream fileIn = new FileInputStream ("./Excel.xlsx"); Workbook workbook = WorkbookFactory.create(fileIn); ) { Sheet sheet = workbook.getSheetAt(0); boolean parallel = false; //boolean parallel = true; Spliterator<Row> spliterator = sheet.spliterator(); Stream<Row> stream = StreamSupport.stream(spliterator, parallel); Map<String, Long> codeMap = stream.skip(1) .map(row -> dataFormatter.formatCellValue(row.getCell(0))) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) ; System.out.println(codeMap.getClass()); System.out.println(codeMap); } catch(Exception ex) { ex.printStackTrace(); } } }
提示:

stream.skip(1)

 是跳过标题行。

使用该代码,您可以测试并行运行是否会产生任何影响。应该不会有什么重大影响。

为什么不简单地始终使用并行流?

使用并行流并不总是比使用顺序流更快。同步会带来自身的成本。因此,决定是否使用并行流的第一个标准是基准测试。没有办法了。

相反,并行运行可能容易出错,因为默认情况下 Java Collections 框架和 Apache POI 都不同步。

在上面的示例中,

Stream.collect 默认情况下是线程安全的,即使 Java Collections 框架(即 java.util.HashMap

)不是线程安全的。并且 
Stream.map
Function 仅从工作簿的一张纸中读取。即使 Apache POI 默认情况下不是线程安全的,这也应该是线程安全的。

因此,使用

boolean parallel = false;

 以及 
boolean parallel = true;
 运行上面的示例应该不会出现问题。但决定是否使用并行流的首要标准是基准测试。如果没有任何优势,为什么要使用更容易出错的方法?

© www.soinside.com 2019 - 2024. All rights reserved.