Apache POI 是 Apache 軟件基金會提供的 100% 開源庫。支持 Excel 庫的全部基本功能。html
圖片來源:易百教程java
在 POI 中,Workbook表明着一個 Excel 文件(工做簿),Sheet表明着 Workbook 中的一個表格,Row 表明 Sheet 中的一行,而 Cell 表明着一個單元格。 HSSFWorkbook對應的就是一個 .xls 文件,兼容 Office97-2003 版本。 XSSFWorkbook對應的是一個 .xlsx 文件,兼容 Office2007 及以上版本。 在 HSSFWorkbook 中,Sheet接口 的實現類爲 HSSFSheet,Row接口 的實現類爲HSSFRow,Cell 接口的實現類爲 HSSFCell。 XSSFWorkbook 中實現類的命名方式相似,在 Sheet、Row、Cell 前加 XSSF 前綴便可。git
<!-- 基本依賴,僅操做 xls 格式只需引入此依賴 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.14</version>
</dependency>
<!-- 使用 xlsx 格式須要額外引入此依賴 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
複製代碼
使用 POI 的目的就是爲了在 Java 中解析/操做 Excel 表格,實現 Excel 的導入/導出的功能,接下來咱們依次來看它們的實現代碼及注意事項。程序員
導出操做即便用 Java 寫出數據到 Excel 中,常見場景是將頁面上的數據(多是通過條件查詢的)導出,這些數據多是財務數據,也多是商品數據,生成 Excel 後返回給用戶下載文件。 該操做主要涉及 Excel 的建立及使用流輸出的操做,在 Excel 建立過程當中,可能還涉及到單元格樣式的操做。github
進行導出操做的第一步是建立 Excel 文件,咱們寫一個方法,參數是須要寫入 Excel 表格的數據和生成 Excel 方式(HSSF,XSSF),返回一個 Workbook 接口對象。 在方法內部咱們採用反射來建立 Workbook 的實例對象。數據庫
探索階段,咱們先將數據類型限定爲 List,並把列數限定爲某個數字,生成一個表格。 代碼以下:apache
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.util.List;
/** * Excel 工廠類,負責 Workbook 的生成和解析 * * @author calmer * @since 2018/12/5 11:19 */
public class ExcelFactory {
/** * 構造 Workbook 對象,具體實例化哪一種對象由 type 參數指定 * @param data 要導出的數據 * @param type Excel 生成方式 * @return 對應 type 的工做簿實例對象 * @throws Exception 反射生成對象時出現的異常 * <li>InstantiationException</li> * <li>IllegalAccessException</li> * <li>InstantiationException</li> */
public static Workbook createExcel(List data,String type) throws Exception{
//根據 type 參數生成工做簿實例對象
Workbook workbook = (Workbook) Class.forName(type).newInstance();
//這裏還能夠指定 sheet 的名字
//Sheet sheet = workbook.createSheet("sheetName");
Sheet sheet = workbook.createSheet();
// 限定列數
int cols = 10;
int rows = data.size() / cols;
int index = 0;
for (int rowNum = 0; rowNum < rows; rowNum++) {
Row row = sheet.createRow(rowNum);
for (int colNum = 0; colNum < cols; colNum++) {
Cell cell = row.createCell(colNum);
cell.setCellValue(data.get(index++).toString());
}
}
return workbook;
}
}
複製代碼
調用時,咱們生成好數據並構造好 Workbook 對象,再調用 Workbook 的 write(OutputStream stream) 方法生成 Excel 文件。數組
List<String> strings = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
strings.add(Integer.toString(i+1));
}
FileOutputStream out = new FileOutputStream("F:\\testXSSF.xlsx");
ExcelFactory.createExcel(strings,"org.apache.poi.xssf.usermodel.XSSFWorkbook").write(out);
out.close();
複製代碼
生成結果:數據結構
以上代碼已經完成簡單的 Excel 文件生成操做,但其中還有幾點問題沒有解決框架
咱們已經明確了兩個問題:
咱們先來解決第二個問題,即參數的問題。
首先建立一個註解類
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** * * @author calmer * @since 2018/12/5 12:27 */
@Retention(RetentionPolicy.SOURCE)
public @interface ExcelType {
String HSSF = "org.apache.poi.hssf.usermodel.HSSFWorkbook";
String XSSF = "org.apache.poi.xssf.usermodel.XSSFWorkbook";
}
複製代碼
在方法參數上加上註解
public static Workbook createExcel(List data, @ExcelType String type) throws Exception {
//內容省略
}
複製代碼
調用時
ExcelFactory.createExcel(list,ExcelType.HSSF).write(out);
複製代碼
關於使用註解來限定參數的取值範圍這種方式,我也是偶然看到過,但是這種方式在我這裏編譯器並不會給任何提示,我對註解瞭解不夠,之後有機會要再好好研究一下。
在實際應用中,很常見的狀況是咱們有不少實體類,好比 Person,Product,Order 等,藉助反射,咱們能夠獲取任意實體類的屬性列表、getter 方法,因此目前,我打算利用反射,來處理多個對象的 Excel 導出。 首先咱們建立一個方法,用來獲取某個對象的屬性列表(暫時不考慮要獲取父類屬性的狀況)。
/** * 獲取對象的屬性名數組 * @param clazz Class 對象,用於獲取該類的信息 * @return 該類的全部屬性名數組 */
private static String[] getFieldsName(Class clazz){
Field[] fields = clazz.getDeclaredFields();
String[] fieldNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldNames[i] = fields[i].getName();
}
return fieldNames;
}
複製代碼
而後咱們完善 createExcel() 方法
public static Workbook createExcel(List data, @ExcelType String type) throws Exception {
if(data == null || data.size() == 0){
throw new Exception("數據不能爲空");
}
//根據類型生成工做簿
Workbook workbook = (Workbook) Class.forName(type).newInstance();
//新建表格
Sheet sheet = workbook.createSheet();
//生成表頭
Row thead = sheet.createRow(0);
String[] fieldsName = getFieldsName(data.get(0).getClass());
for (int i = 0; i < fieldsName.length; i++) {
Cell cell = thead.createCell(i);
cell.setCellValue(fieldsName[i]);
}
//保存全部屬性的getter方法名
Method[] methods = new Method[fieldsName.length];
for (int i = 0; i < data.size(); i++) {
Row row = sheet.createRow(i+1);
Object obj = data.get(i);
for (int j = 0; j < fieldsName.length; j++) {
//加載第一行數據時,初始化全部屬性的getter方法
if(i == 0){
String fieldName = fieldsName[j];
//處理布爾值命名 "isXxx" -> "setXxx"
if (fieldName.contains("is")) {
fieldName = fieldName.split("is")[1];
}
methods[j] = obj.getClass().getMethod("get" +
fieldName.substring(0,1).toUpperCase() +
fieldName.substring(1));
}
Cell cell = row.createCell(j);
Object value = methods[j].invoke(obj);
//注意判斷 value 值是否爲空
if(value == null){
value = "無";
}
cell.setCellValue(value.toString());
}
}
return workbook;
}
複製代碼
以上代碼基本知足一開始的需求,即以類的屬性名爲表頭並生成表格。接下來咱們生成必定量的數據,並測試導出效果。 實體類代碼
/** * * @author calmer * @since 2018/12/5 14:50 */
public class Person {
private Integer id;
private String name;
private Integer age;
private String hobby;
private String job;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
複製代碼
測試類代碼
List<Person> list = new ArrayList<>();
for (int i = 0; i < 60000; i++) {
int num = i + 1;
Person person = new Person();
person.setId(num);
person.setName("張三-"+(num));
person.setAddress("花園路"+num+"號"+(int)Math.ceil(Math.random()*10)+"號樓");
person.setAge(i+18);
person.setHobby("洗臉刷牙打DOTA");
person.setJob("程序員");
list.add(person);
}
FileOutputStream out = new FileOutputStream("F:\\testXSSF.xlsx");
ExcelFactory.createExcel(list,ExcelType.XSSF).write(out);
out.close();
複製代碼
生成的結果以下
這裏測試的時候我使用6W的數據,因此程序進行的比較慢,用時以下:
像這種大數據量的導出,咱們可使用 SXSSF 的方式,網上也有不少例子,官網的對比。使用 SXSSF 方式導出用時以下:
能夠看到時間縮短了不少。接下來咱們單獨來了解一下如何控制表格的樣式。
一般,咱們須要控制的樣式有兩個部分,一個是表頭部分的樣式,另外一個是普通單元格的樣式。此次咱們就僅建立兩個方法演示樣式的設置方式。 在 POI 中,控制單元格樣式的對象是 CellStyle 接口,能夠經過 Workbook 的createStyle 方法得到實例對象,這裏咱們寫一個方法設置表頭的樣式。
private static CellStyle getTheadStyle(Workbook workbook){
CellStyle style = workbook.createCellStyle();
//設置填充色
style.setFillForegroundColor(IndexedColors.LIGHT_BLUE.index);
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
//設置對齊方式
style.setAlignment(CellStyle.ALIGN_CENTER);
//字體樣式
Font font = workbook.createFont();
//設置字體名稱
font.setFontName("華文隸書");
//斜體
font.setItalic(true);
//字體顏色
font.setColor(IndexedColors.YELLOW.index);
//字體大小
font.setFontHeightInPoints((short)12);
//不要忘記這句
style.setFont(font);
return style;
}
複製代碼
調用
Row thead = sheet.createRow(0);
//設置行高
thead.setHeight((short) 500);
//僅使用 setRowStyle 方法會對除有值的表頭設置樣式
thead.setRowStyle(style);
String[] fieldsName = getFieldsName(data.get(0));
for (int i = 0; i < fieldsName.length; i++) {
Cell cell = thead.createCell(i);
cell.setCellValue(fieldsName[i]);
//在這裏循環爲每一個有值的表頭設置樣式。
//結合上面的 setRowStyle 會將表頭行所有設置樣式
cell.setCellStyle(style);
}
複製代碼
接下來咱們寫獲取普通單元格樣式的方法
private static CellStyle getCommonStyle(Workbook workbook){
CellStyle style = workbook.createCellStyle();
//設置填充色
style.setFillForegroundColor(IndexedColors.GREEN.index);
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
//設置居中對齊
style.setAlignment(CellStyle.ALIGN_CENTER);
Font font = workbook.createFont();
font.setFontName("華文彩雲");
//不要忘記這句
style.setFont(font);
return style;
}
複製代碼
完整調用
public static Workbook createExcel(List data, @ExcelType String type) throws Exception {
if(data == null || data.size() == 0){
throw new Exception("數據不能爲空");
}
//根據類型生成工做簿
Workbook workbook = (Workbook) Class.forName(type).newInstance();
//生成樣式
CellStyle style = getTheadStyle(workbook);
//新建表格
Sheet sheet = workbook.createSheet();
//生成表頭
Row thead = sheet.createRow(0);
//設置行高
thead.setHeight((short) 500);
//僅使用 setRowStyle 方法會對除有值的表頭設置樣式
thead.setRowStyle(style);
String[] fieldsName = getFieldsName(data.get(0));
for (int i = 0; i < fieldsName.length; i++) {
Cell cell = thead.createCell(i);
cell.setCellValue(fieldsName[i]);
//在這裏循環爲每一個有值的表頭設置樣式。
//結合上面的 setRowStyle 會將表頭行所有設置樣式
cell.setCellStyle(style);
}
//保存全部屬性的getter方法名
Method[] methods = new Method[fieldsName.length];
//獲取普通單元格樣式
style = getCommonStyle(workbook);
for (int i = 0; i < data.size(); i++) {
Row row = sheet.createRow(i+1);
Object obj = data.get(i);
for (int j = 0; j < fieldsName.length; j++) {
//加載第一行數據時,初始化全部屬性的getter方法
if(i == 0){
String fieldName = fieldsName[j];
methods[j] = obj.getClass().getMethod("get" +
fieldName.substring(0,1).toUpperCase() +
fieldName.substring(1));
}
Cell cell = row.createCell(j);
Object value = methods[j].invoke(obj);
//注意判斷 value 值是否爲空
if(value == null){
value = "無";
}
cell.setCellValue(value.toString());
//設置單元格樣式
cell.setCellStyle(style);
}
}
return workbook;
}
複製代碼
生成結果以下(忽視顏色搭配與美觀程度)
這裏我運行的出了一個問題,在此記錄。 注意上面代碼的第 28 行和第 48 行,這裏咱們在 for 循環外面獲取 Style 對象,在 for 循環中循環設置單元格樣式的時候,始終使用的是__同一個__ Style。而最開始我測試的時候,並非這樣寫,而是像下面這樣:
for (int i = 0; i < data.size(); i++) {
Row row = sheet.createRow(i+1);
Object obj = data.get(i);
for (int j = 0; j < fieldsName.length; j++) {
//加載第一行數據時,初始化全部屬性的getter方法
if(i == 0){
String fieldName = fieldsName[j];
methods[j] = obj.getClass().getMethod("get" +
fieldName.substring(0,1).toUpperCase() +
fieldName.substring(1));
}
Cell cell = row.createCell(j);
Object value = methods[j].invoke(obj);
//注意判斷 value 值是否爲空
if(value == null){
value = "無";
}
cell.setCellValue(value.toString());
//設置單元格樣式
cell.setCellStyle(getCommonStyle(workbook));
}
}
複製代碼
注意 20 行,在 getCommonStyle 方法中,咱們每次調用都會使用 Workbook 對象建立一個 Style 對象,而咱們的數據一共有 6W 條,沒條數據又有 6 個屬性,咱們一共要渲染 36W 個單元格,也就是要生成 36W 個 Style 對象。因而,在我運行代碼時便出現了以下報錯。
F:\java\jdk1.8.0_151\bin\java.exe
Exception in thread "main" java.lang.IllegalStateException: The maximum number of Cell Styles was exceeded. You can define up to 64000 style in a .xlsx Workbook
at org.apache.poi.xssf.model.StylesTable.createCellStyle(StylesTable.java:789)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.createCellStyle(XSSFWorkbook.java:682)
at org.apache.poi.xssf.streaming.SXSSFWorkbook.createCellStyle(SXSSFWorkbook.java:869)
at com.xhc.study.util.poi.ExcelFactory.getCommonStyle(ExcelFactory.java:114)
at com.xhc.study.util.poi.ExcelFactory.createExcel(ExcelFactory.java:73)
at Test.main(Test.java:62)
Process finished with exit code 1
複製代碼
這裏提示咱們最多讓一個 Workbook 對象生成 64000 個 Style 對象。 之後一些危險的操做仍是少作😉
導入操做即便用 Java 讀取 Excel 中的數據,常見場景是在頁面上點擊導入按鈕,用戶選擇 Excel 文件,其中多是多條商品數據(包含編號、名稱、參數等信息),經過文件上傳功能將 Excel 讀取到咱們的程序中,解析其中的數據並存入數據庫中。
導入操做主要依靠 Workbook 的一個構造函數,源碼以下
/** * Constructs a XSSFWorkbook object, by buffering the whole stream into memory * and then opening an {@link OPCPackage} object for it. * * <p>Using an {@link InputStream} requires more memory than using a File, so * if a {@link File} is available then you should instead do something like * <pre><code> * OPCPackage pkg = OPCPackage.open(path); * XSSFWorkbook wb = new XSSFWorkbook(pkg); * // work with the wb object * ...... * pkg.close(); // gracefully closes the underlying zip file * </code></pre> */
public XSSFWorkbook(InputStream is) throws IOException {
super(PackageHelper.open(is));
beforeDocumentRead();
// Build a tree of POIXMLDocumentParts, this workbook being the root
load(XSSFFactory.getInstance());
// some broken Workbooks miss this...
if(!workbook.isSetBookViews()) {
CTBookViews bvs = workbook.addNewBookViews();
CTBookView bv = bvs.addNewWorkbookView();
bv.setActiveTab(0);
}
}
複製代碼
從這個構造函數來看,咱們只需提供一個輸入流,便能構造一個 Workbook 對象出來,接下來咱們首先寫一個處理 Workbook 的方法,參數爲一個 Workbook 對象,咱們在方法內部遍歷表格並輸出數據,這裏咱們默認該文件是一個規則的表格,即符合咱們以前生成的 Excel 那樣的格式。代碼以下
/** * 讀取 Excel 數據並處理 * @param workbook 完整的 Workbook 對象 */
public static void readExcel(Workbook workbook) {
Sheet sheet = workbook.getSheetAt(0);
//獲取總行數
int rows = sheet.getPhysicalNumberOfRows();
//去除表頭,從第 1 行開始打印
for (int i = 0; i < rows; i++) {
Row row = sheet.getRow(i);
//獲取總列數
int cols = row.getPhysicalNumberOfCells();
for (int j = 0; j < cols; j++) {
System.out.print(row.getCell(j) + "\t");
}
System.out.println();
}
}
複製代碼
爲了輸出方便,我已將 Excel 中的數據降爲 100 條。調用代碼以下
FileInputStream in = new FileInputStream("F:\\testXSSF.xlsx");
XSSFWorkbook workbook = new XSSFWorkbook(in);
ExcelFactory.readExcel(workbook);
in.close();
複製代碼
輸出結果以下
數據已經拿到,接下來的問題是解析爲對象,畢竟咱們平時向數據庫保存數據使用的 ORM 框架通常都使用了傳輸對象。這裏咱們再次利用反射,完善代碼,使 readExcel 方法有讀取 Excel 中的數據並將其映射爲對象的能力。
這裏須要明確幾個問題
接下來咱們開始完善 readExcel 方法,代碼以下
/** * 讀取 Excel 數據並處理 * * @param workbook 完整的 Workbook 對象 * @param clazz Excel 中存儲的數據的類的 Class 對象 * @param <T> 泛型 * @return 解析以後的對象列表,與泛型一致 * @throws Exception */
public static <T> List<T> readExcel(Workbook workbook, Class<T> clazz) throws Exception {
List<T> list = new ArrayList<>();
Sheet sheet = workbook.getSheetAt(0);
//獲取總行數
int rows = sheet.getPhysicalNumberOfRows();
//獲取全部字段名
String[] fieldsName = getFieldsName(clazz);
Method[] methods = new Method[fieldsName.length];
//去除表頭,從第 1 行開始打印
for (int i = 1; i < rows; i++) {
T obj = clazz.newInstance();
Row row = sheet.getRow(i);
//獲取總列數
int cols = row.getPhysicalNumberOfCells();
//獲取全部屬性
Field[] fields = clazz.getDeclaredFields();
//處理對象的每個屬性
for (int j = 0; j < cols; j++) {
//第一次循環時初始化全部 setter 方法名
if (i == 1) {
String fieldName = fieldsName[j];
//處理布爾值命名 "isXxx" -> "setXxx"
if (fieldName.contains("is")) {
fieldName = fieldName.split("is")[1];
}
methods[j] = obj.getClass().getMethod("set" +
fieldName.substring(0, 1).toUpperCase() +
fieldName.substring(1), fields[j].getType());
}
//先將單元格中的值按 String 保存
String param = row.getCell(j).getStringCellValue();
//屬性的類型
String typeName = fields[j].getType().getSimpleName();
//set 方法
Method method = methods[j];
//排除空值
if (param == null || "".equals(param)) {
continue;
}
//根據對象的不一樣屬性字段轉換單元格中的數據類型並調用 set 方法賦值
if ("Integer".equals(typeName) || "int".equals(typeName)) {
method.invoke(obj, Integer.parseInt(param));
} else if ("Date".equals(typeName)) {
String pattern;
if (param.contains("CST")) {
//java.util.Date 的默認格式
pattern = "EEE MMM dd HH:mm:ss zzz yyyy";
} else if (param.contains(":")) {
//帶有時分秒的格式
pattern = "yyyy-MM-dd HH:mm:ss";
} else {
//簡單格式
pattern = "yyyy-MM-dd";
}
method.invoke(obj, new SimpleDateFormat(pattern, Locale.UK).parse(param));
} else if ("Long".equalsIgnoreCase(typeName)) {
method.invoke(obj, Long.parseLong(param));
} else if ("Double".equalsIgnoreCase(typeName)) {
method.invoke(obj, Double.parseDouble(param));
} else if ("Boolean".equalsIgnoreCase(typeName)) {
method.invoke(obj, Boolean.parseBoolean(param));
} else if ("Short".equalsIgnoreCase(typeName)) {
method.invoke(obj, Short.parseShort(param));
} else if ("Character".equals(typeName) || "char".equals(typeName)) {
method.invoke(obj, param.toCharArray()[0]);
} else {
//若數據格式爲 String 則沒必要轉換
method.invoke(obj, param);
}
}
//不要忘記這句
list.add(obj);
}
return list;
}
複製代碼
接下來咱們改造 Person 類,添加幾個不一樣類型的數據,並加入 toString() 方法,供咱們測試使用。
import java.util.Date;
/** * * @author calmer * @since 2018/12/5 14:50 */
public class Person {
private Integer id;
private String name;
private Integer age;
private String hobby;
private String job;
private String address;
private Date birthday;
private Character sex;
private Long phone;
private Boolean isWorked;
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
", job='" + job + '\'' +
", address='" + address + '\'' +
", birthday=" + birthday +
", sex=" + sex +
", phone=" + phone +
", isWorked=" + isWorked +
'}';
}
public Long getPhone() {
return phone;
}
public void setPhone(Long phone) {
this.phone = phone;
}
public Boolean getWorked() {
return isWorked;
}
public void setWorked(Boolean worked) {
isWorked = worked;
}
public Character getSex() {
return sex;
}
public void setSex(Character sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
複製代碼
接下來是測試調用的代碼,咱們直接將導出與導入兩段代碼一塊兒執行。
public static void main(String[] args) throws Exception {
//生成數據
List<Person> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
int num = i + 1;
Person person = new Person();
person.setId(num);
person.setName("張三-"+(num));
person.setAddress("花園路"+num+"號"+(int)Math.ceil(Math.random()*10)+"號樓");
person.setAge(i+18);
person.setHobby("洗臉刷牙打DOTA");
person.setJob("程序員");
person.setBirthday(new Date());
person.setSex('男');
person.setPhone(4536456498778789123L);
person.setWorked(true);
list.add(person);
}
//導出 Excel
FileOutputStream out = new FileOutputStream("F:\\testXSSF.xlsx");
ExcelFactory.createExcel(list,ExcelType.SXSSF).write(out);
out.close();
//導入 Excel
FileInputStream in = new FileInputStream("F:\\testXSSF.xlsx");
XSSFWorkbook workbook = new XSSFWorkbook(in);
List<Person> personList = ExcelFactory.readExcel(workbook,Person.class);
in.close();
//遍歷結果
for (Person person : personList) {
System.out.println(person);
}
}
複製代碼
執行結果以下:
功能已經基本實現,咱們此次再試一下在大數據導入的情景下,程序的耗時如何。咱們此次一樣適用 6W 條數據。結果以下
這裏咱們能夠看到,導入操做佔用的內存和耗時,都比導出操做多不少。在導出的時候咱們知道 POI 在導出大數據量的時候提供了 SXSSF 的方式解決耗時和內存溢出問題,那麼在導入時是否是也會有某種方式能夠解決這個問題呢?
關於這部分的代碼,能夠在網上找到許多,本次暫不討論。另外據說有一個 EasyExcel 挺好用的,有時間試一下。
經過此次探索,深知本身不足的地方還不少,原來寫代碼的時候考慮的太少,有關效率,內存使用等方面的問題在本身測試的時候是看不出來的,真正使用的時候這些問題纔會暴露出來,好比某項操做可能會致使用戶幾十秒甚至幾分鐘的等待,或者程序直接崩掉。 因此之後仍是要當心謹慎,對工具類的使用不能會用就夠,要儘可能的深刻研究。 道可頓悟,事需漸修。