最近在公司有人反映说之前用VBA写的排序功能效率太低,执行一个1w条数据的文件需要5Min,所以就想用java将其改写以提高效率。
需求:其有一个DataSheet,和一个RuleSheet,其中RuleSheet中每一行都对应一个排序规则,符合条件且越在前的排序规则出来的数据在拍完序后就应该出现在结果表的前面
RuleSheet中大概有800个规则。
既然涉及到读写Excel,就使用POI工具了。因为是2003,就使用了如下基本操作代码:
1 File excelFile = new File(path); 2 FileInputStream fis = new FileInputStream(excelFile); 3 Workbook wb = new HSSFWorkbook(fis); 4 fis.close();
取行数:
sh.getLastRowNum() + 1;
sh即为Sheet类,因为Excel和java计数不同的原因,Excel从1开始计数,java从0开始,所以如果你在Excel中共有3行的话,getLastRowNum得到的值即为2(这么做只是为了符合自己写循环的习惯)。
取列数:因为表头的列数是最全的,所以以后循环读取的时候,最好用表头的列数,取列数方法:
colMax = row.getLastCellNum();
getLastCellNum() 方法返回的即为Excel中实际的列数:即Excel中如果有4列,那么这个方法返回值即为4,同【行】的取出值略有不同。
之后就是循环每一个Cell取值:取值的时候用了一个非常粗糙的方法:
cell.toString();
导致后来写文件的时候出现了科学计数法,且使用了大量的科学计数法的转换方式:
new BigDecimal(this.getContractNo()).toPlainString()
网上找的大部分都是toString();方法,亲测表示如1230000,toString()无法成功转换。
接下来就是排序了,因为排序规则实在太多,而且规则中的字段大部分都不一样,故在鄙人百般思考下仍然采用了最笨的排序方法:挨个排序,排一个删一条。
望各位如果好办法请提出。
由于循环排序实在无聊,就不写了,只是其中为了能够支持List中一边删除一边取数的要求使用了dataLst.listIterator();因为此方法不是快速失败的,正好满足需求。
排完序了,就准备开始写数据:
开始我还很天真地认为生成的wb会自动保存至Excel文件...
好了,写文件,因为Excel 的机制是sheet页内不保存真正的值,只保留其sheet副本中的值的位置(请参考:),故需要逐个写Cell并设置值,循环设置值,毫无新意,但是在遇到诸如:185403213的值时,POI又会将其写成科学计数法,解决办法:设置Cell为文本格式即可。设置方法:
CellStyle cellStyle = wbb.createCellStyle();
DataFormat df = wbb.createDataFormat();
cellStyle.setDataFormat(df.getFormat("@"));
当然如果你觉的自己记Style太麻烦可以使用如下代码:
1 // 控制单元格格式 2 CellStyle cellStyle = wbb.createCellStyle(); 3 DataFormat df = wbb.createDataFormat(); 4 cellStyle.setDataFormat(df.getFormat(BuiltinFormats.getBuiltinFormat(48)));
cell.setCellStyle(cellStyle);
这样写进去的内容就和内存中的内存完全一致了,并且Excel中单元格也变为文本格式。
使用HSSFWorkbook去写只能写2003格式,但是由于数据量太大,故使用了POI中专门处理大数据量的类:SXSSFWorkbook
此类在新建的时候可以指定内存中最大的存储条数如:
/** 更改写入方式 每次1000行 */
Workbook wbb = new SXSSFWorkbook(1000);
使用方式由于都是Workbook的子类,使用方式一样。
待我将类测试完了,打成jar包,高高兴兴地双击执行的时候却发现:
1 @echo start... 2 java MyTools.jar 3 @pause
1 start... 2 3 E:\LTQ\其他系统\VMI\测试>java -jar MyTools.jar 4 开始打开文件:2014-06-06 15:24:45.260 5 Exception in thread "main" java.lang.reflect.InvocationTargetException 6 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 7 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl. 8 java:39) 9 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces 10 sorImpl.java:25) 11 at java.lang.reflect.Method.invoke(Method.java:597) 12 at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoa 13 der.java:58) 14 Caused by: java.lang.OutOfMemoryError: Java heap space 15 at java.lang.Integer.valueOf(Integer.java:585) 16 at org.apache.poi.hssf.record.aggregates.RowRecordsAggregate.insertRow(R 17 owRecordsAggregate.java:127) 18 at org.apache.poi.hssf.record.aggregates.RowRecordsAggregate.<init>(RowR 19 ecordsAggregate.java:87) 20 at org.apache.poi.hssf.model.InternalSheet.<init>(InternalSheet.java:208 21 ) 22 at org.apache.poi.hssf.model.InternalSheet.createSheet(InternalSheet.jav 23 a:163) 24 at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:2 25 96) 26 at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:2 27 48) 28 at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:1 29 92) 30 at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:3 31 27) 32 at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:3 33 08) 34 at org.jingqt.main.MainFun.main(MainFun.java:49) 35 ... 5 more 36 请按任意键继续. . .
内存溢出。。。
在eclipse中无论怎么执行都是非常正常的,打成jar包就无法运行,虽说C#是windows亲儿子,但是也不至于排斥java呀!网上搜了好久没有好的解决办法,也不清楚为什么。
最后发现eclipse的内存设置在其eclipse.ini中写好的,但是JVM的内存设置就是默认的了,默认就比较低了,故执行时添加设置内存的参数如下:
1 @echo start... 2 java -Xms128m -Xmx512m -jar MyTools.jar 3 @pause
如此就可以正确执行了。
以上就是初次使用POI的经验,本不上台面的,但是做为笔记感觉还是有必要写出来供大家参考一下。
附录:JVM内存设置http://hi.baidu.com/ikqxuzfdtxefpwr/item/075449137c40124fe75e06b2
浙公网安备 33010602011771号