Java 處理表格,真的很爽!
一個(gè)簡(jiǎn)單又快速的表格處理庫(kù)
大家好,我是魚皮。
處理 Excel 表格是開發(fā)中經(jīng)常遇到的需求,比如表格合并、篩選表格中的某些行列、修改單元格數(shù)據(jù)等。
今天給大家分享一個(gè) Java 處理表格的工具庫(kù),不需要任何專業(yè)知識(shí),拿來(lái)就能用,快速又輕松~

可能有同學(xué)說(shuō)了,用 Python 處理表格不是更方便么?為毛用 Java ???
當(dāng)然是因?yàn)槠髽I(yè)中大部分后臺(tái)開發(fā)用的都是 Java!如果你要搞一個(gè)允許用戶自主上傳 Excel 進(jìn)行處理的服務(wù),那顯然直接用 Java 來(lái)實(shí)現(xiàn)最方便~

Easy Excel
要介紹的庫(kù)是阿里的 Easy Excel,簡(jiǎn)單、省內(nèi)存的讀寫 Excel 的開源項(xiàng)目。
文檔地址:https://www.yuque.com/easyexcel/doc/easyexcel
直接打開官方文檔,就能看到項(xiàng)目的使用說(shuō)明了:

首先在項(xiàng)目中引入 Easy Excel(版本號(hào)以文檔中的最新版本號(hào)為主):
<dependency>
????<groupId>com.alibaba</groupId>
????<artifactId>easyexcel</artifactId>
????<version>3.0.5</version>
</dependency>
然后進(jìn)入文檔的 快速開始 部分,就可以看到讀取和寫入表格數(shù)據(jù)的方法了。
下面讓我們以一個(gè)實(shí)際需求為例,試著使用一下這個(gè)庫(kù)。
需求
假設(shè)我們有這樣一個(gè) Excel 表格:

如果想要調(diào)換 姓名列 和 年齡列 的順序,應(yīng)該怎么做呢?
讀取表格
首先要讀取原始表格中的數(shù)據(jù)。
Easy Excel 提供了兩種讀取表格的方式:創(chuàng)建對(duì)象的讀 和 不創(chuàng)建對(duì)象的讀 。
創(chuàng)建對(duì)象的讀
如果你已知整個(gè)表格的表頭信息,比如列名(比如 “姓名”)和列的數(shù)據(jù)類型(比如字符串),那么可以創(chuàng)建一個(gè)對(duì)應(yīng)的類,用來(lái)在 Java 中表示表格的元信息。
比如為上述表格創(chuàng)建 YupiData
類,代碼如下:
public?class?YupiData?{
??//?姓名
??private?String?name;
??//?年齡
??private?Integer?age;
??//?出生日期
??private?Date?bornDate;
}
默認(rèn)會(huì)根據(jù)屬性的順序來(lái)關(guān)聯(lián)表格列的順序,比如 name 對(duì)應(yīng)姓名(第 0 列)、age 對(duì)應(yīng)年齡(第 1 列)。
當(dāng)然,你也可以使用注解的方式來(lái)指定每個(gè)屬性對(duì)應(yīng)的表格列,支持指定下標(biāo)和列名,代碼如下:
public?class?YupiData?{
??//?強(qiáng)制讀取下標(biāo)為?2?的列(第三列)
?? (index?=?2)
??//?指定接受日期的格式
?? ("yyyy/MM/dd")
??private?Date?bornDate;
????
??//?用名字去匹配,不能和其他列重復(fù)
?? ("年齡")
??private?Integer?age;
????
?? ("姓名")
??private?String?name;
}
定義好了表格數(shù)據(jù)類,就可以開始讀取了,該庫(kù)非常貼心,提供了 同步 和 異步 兩種讀取方式。
同步是指一次性讀取表格中的所有行,以列表的方式完整返回,再整體去處理。由于這種方式會(huì)將數(shù)據(jù)完整加載到內(nèi)存中,因此只 適用于表格行數(shù)比較少 的情況。代碼如下:
/**
?*?同步讀取
?*/
public?void?synchronousRead()?{
??String?fileName?=?"魚皮的表格.xlsx";
??//?讀取到的數(shù)據(jù)
??List<YupiData>?list?=?EasyExcel.read(fileName)
???.head(YupiData.class)
????.sheet()
????.doReadSync();
}
異步方式需要定義一個(gè) 監(jiān)聽器 ,每讀取一行,就要立即去處理該行數(shù)據(jù)。這樣就不需要將所有數(shù)據(jù)都加載到內(nèi)存中,算一行讀一行,理論上算完了也可以丟棄。代碼如下:
/**
?*?定義監(jiān)聽器
?*/?
public?class?YupiDataListener?
????implements?ReadListener<YupiData>?{
??/**
???*?每讀一行數(shù)據(jù),都會(huì)調(diào)用一次
???*
???*?@param?data?一行數(shù)據(jù)
???*?@param?context?上下文
???*/
??
??public?void?invoke(YupiData?data,?AnalysisContext?context)?{
????//?輸出姓名
????System.out.println(data.getName());
??}
}
/**
?*?開始讀取
?*/
void?assynchronousRead()?{
??String?fileName?=?"魚皮的表格.xlsx";
??EasyExcel.read(fileName,?YupiData.class,
??????new?YupiDataListener())
??????.sheet()
??????.doRead();
}
不創(chuàng)建對(duì)象的讀
如果事先不清楚表格會(huì)有哪些列、類型如何(比如讓用戶自主上傳表格),那么可以使用 不創(chuàng)建對(duì)象讀 的方式,直接用 Map<Integer, String>
泛型類來(lái)接收:
List<Map<Integer,?String>>?list?=?EasyExcel
????.read(fileName)
????.sheet()
????.doReadSync();
//?Map?的?key?為列下標(biāo),value?為單元格的值
for?(Map<Integer,?String>?data?:?list)?{
?...?
}
當(dāng)然,這種讀取方式也同時(shí)支持同步和異步,可以根據(jù)需求選擇方式,靈活的一批!
寫入表格
學(xué)會(huì)讀取后,寫入表格就更簡(jiǎn)單了,依然是先定義一個(gè)類,用來(lái)表示要寫入表格的元信息(列名、列數(shù)據(jù)類型等)。
比如要完成表格列順序調(diào)換的需求,定義表格數(shù)據(jù)類的時(shí)候,把 age 和 name 屬性的順序換一下就好了:
public?class?YupiWriteData?{
??//?年齡?↑
??private?Integer?age;
??//?姓名?↓
??private?String?name;
??//?出生日期
??private?Date?bornDate;
}
然后執(zhí)行 Easy Excel 的 write 方法,就完事了,代碼如下:
void?doWrite()?{
??//?已讀取和處理后的數(shù)據(jù)列表
??List<YupiWriteData>?dataList?=?xxx;
??String?fileName?=?"result.xlsx";
??EasyExcel.write(fileName,?YupiWriteData.class)
??????.sheet("工作表1")
??????.doWrite(dataList);
}
搞定,是不是賊簡(jiǎn)單!
除了這個(gè)庫(kù)外,Java 處理 Excel 的庫(kù)還有很多,比如 Apache POI、Hutool 等,大家可以去試試。但我個(gè)人感覺(jué)還是 Easy Excel 更對(duì)我的胃口。
好了,是不是很簡(jiǎn)單了,有興趣的話自己寫個(gè)表格處理程序吧~
學(xué)到的話,幫魚皮點(diǎn)個(gè) 贊 唄,感謝!
