| 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.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;
运行上面的示例应该不会出现问题。但决定是否使用并行流的首要标准是基准测试。如果没有任何优势,为什么要使用更容易出错的方法?