Apache poi 加速apachepoi-SUMIF

Apache poi 加速apachepoi-SUMIF,apache-poi,apache-poi-4,Apache Poi,Apache Poi 4,在xlsx工作簿中,有些单元格具有一些无界的SUMIF公式,如下所示:SUMIF(MySheetname!$B:$B,$E4,MySheetname!$I:$I)。 使用ApachePOI5.0.0对一个SUMIF函数的评估持续100ms,对给定工作簿的评估持续几分钟 提高执行持续时间的一种方法是将公式绑定到如下内容:SUMIF(MySheetname!$B1:$B100,$E4,MySheetname!$I1:$I100)。在我的情况下,这不是一个解决方案,因为我不是xlsx文件的作者,系统从

在xlsx工作簿中,有些单元格具有一些无界的SUMIF公式,如下所示:
SUMIF(MySheetname!$B:$B,$E4,MySheetname!$I:$I)
。 使用ApachePOI5.0.0对一个SUMIF函数的评估持续100ms,对给定工作簿的评估持续几分钟

提高执行持续时间的一种方法是将公式绑定到如下内容:
SUMIF(MySheetname!$B1:$B100,$E4,MySheetname!$I1:$I100)
。在我的情况下,这不是一个解决方案,因为我不是xlsx文件的作者,系统从未知的人那里获取未知的xlsx文件(因此我不能仅仅告诉他们限制SUMIF范围)

org.apache.poi.ss.formula.functions.Sumif的当前实现迭代给定(无界)范围内的所有单元格,因此每次计算迭代1048576个单元格

这是方法
sumMatchingCells(AreaEval、I_MatchPredicate、AreaEval)实现的一部分。

LazyAreaEval
包含一个
SheetRangeEvaluator
,其中包含
SheetRefEvaluator
s,其中包含一个
EvaluationSheet
,这至少知道
getLastRowNum()
。不幸的是,这个属性链是私有的


你知道如何做到这一点吗?或者其他如何提高SUMIF执行性能的想法?

修补ApachePOI公式评估需要深入了解源代码并在评估过程中重新搜索。那不是我要做的

但一种解决方法是,在计算之前,将公式中的所有整列引用替换为表中从第1行到最后一行的区域引用

如果只读取工作簿,则这只会影响随机访问内存,而不会影响存储的文件。当然,如果需要保存已更改的工作簿,则会影响存储的文件。那么解决方案可能不可用

当工作表中有多个公式具有完整列引用时,这对过程持续时间有显著影响,至少使用
*.xlsx
XSSF
)并且需要为每个公式执行额外的替换过程

完整的代码示例:

import java.io.FileInputStream;
导入org.apache.poi.ss.formula.*;
导入org.apache.poi.ss.formula.ptg.*;
导入org.apache.poi.ss.usermodel.*;
导入org.apache.poi.xssf.usermodel.*;
导入org.apache.poi.hssf.usermodel.*;
导入org.apache.poi.ss.SpreadsheetVersion;
公共类ExcelEvaluateFullColumn公式{
私有静态字符串replaceFullColumnReferences(XSSFSheet工作表,字符串公式){
//System.out.println(公式);
XSSFWorkbook工作簿=sheet.getWorkbook();
XSSFEvaluationWorkbook evaluationWorkbook=XSSFEvaluationWorkbook.create(工作簿);
Ptg[]ptgs=FormulaParser.parse(公式,(FormulaParsingWorkbook)评估工作簿,
FormulaType.CELL,sheet.get工作簿().getSheetIndex(sheet));
对于(int i=0;i
注释掉该部分

。。。
/*
if(cell.getCellType()==CellType.FORMULA){
if(sheet instanceof XSSFSheet){//仅对XSSF执行此操作,而对HSSF不必执行此操作。
字符串公式=cell.getCellFormula();
公式=replaceFullColumnReferences((XSSFSheet)表,公式);
cell.setcell公式(公式);
}
}
*/
...

查看差异。

今天中午Axel给出了一个答案,重复了每个公式,并将每个区域ptgbase.lastRow限制在表中的最后一行。在我的例子中,这似乎很好(只要区域从行索引0开始)。一点副作用:
MATCH(G$3,mysheetname!$2:$2,0)
变成了
MATCH(G$3,mysheetname!$A$2:$XFD$2,0)
。LibreOffice 7.0.5.2显示了该beca的名称消息
for (int r=0; r<height; r++) {
    for (int c=0; c<width; c++) {
        result += accumulate(aeRange, mp, aeSum, r, c);
    }
}
for (int r = 0; r < height; r++) {
    if (aeSum.sheetContainsRowIndex(aeSum.getFirstRow() + r)) {
        for (int c = 0; c < width; c++) {
            if (aeSum.sheetContainsColumnIndex(aeSum.getFirstColumn() + c)) {
               [...]