先開(kāi)始接到這個(gè)需求的時(shí)候,第一想法是這得要多復(fù)雜的算才能實(shí)現(xiàn),因?yàn)椴皇敲恳涣械暮喜⒌男袛?shù)一樣,比如第一列1-4行的內(nèi)容都相同,那么需要合并,而第二列1-2相同,所以1和2合并,3-4相同,3和4合并;然后一層一層的,就和樹(shù)一樣,逐層向下;
毋庸置疑,通過(guò)算法肯定能實(shí)現(xiàn),但是我覺(jué)得太繞了,一下子也想不出來(lái)什么好的算法;后來(lái)我就想到約瑟夫環(huán)的問(wèn)題,一個(gè)典型的java面向?qū)ο蟮睦樱缓髞?lái)就通過(guò)建一個(gè)單元格對(duì)象,屬性有單元格內(nèi)容,與單元格同一列的上一行內(nèi)容,起始行,起始列,就這四個(gè)屬性;詳細(xì)解釋我放在代碼中:
model對(duì)象:
* Created by zelei.fan on 2017/3/20. private String oldContent; public String getOldContent() { public void setOldContent(String oldContent) { this.oldContent = oldContent; public String getContent() { public void setContent(String content) { public int getRowIndex() { public void setRowIndex(int rowIndex) { this.rowIndex = rowIndex; public int getCellIndex() { public void setCellIndex(int cellIndex) { this.cellIndex = cellIndex;
Test:
import cn.yoho.perf.common.model.PoiModel; import com.beust.jcommander.internal.Maps; import com.google.common.collect.Lists; 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 org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileOutputStream; import java.io.IOException; * Created by zelei.fan on 2017/3/14. * @param title 標(biāo)題集合 tilte的長(zhǎng)度應(yīng)該與list中的model的屬性個(gè)數(shù)一致 * @param mergeIndex 合并單元格的列 public static String createExcel(String[] title, Map<String/*sheet名*/, List<Map<String/*對(duì)應(yīng)title的值*/, String>>> maps, int[] mergeIndex){ Workbook workbook = new XSSFWorkbook(); for(Map.Entry<String, List<Map<String/*對(duì)應(yīng)title的值*/, String>>> entry : maps.entrySet()){ /*實(shí)例化sheet對(duì)象并且設(shè)置sheet名稱,book對(duì)象*/ sheet = workbook.createSheet(); workbook.setSheetName(n, entry.getKey()); workbook.setSelectedTab(0); /*初始化head,填值標(biāo)題行(第一行)*/ Row row0 = sheet.createRow(0); for(int i = 0; i<title.length; i++){ Cell cell_1 = row0.createCell(i, Cell.CELL_TYPE_STRING); cell_1.setCellValue(title[i]); /*得到當(dāng)前sheet下的數(shù)據(jù)集合*/ List<Map<String/*對(duì)應(yīng)title的值*/, String>> list = entry.getValue(); List<PoiModel> poiModels = Lists.newArrayList(); Iterator iterator = list.iterator(); int index = 1;/*這里1是從excel的第二行開(kāi)始,第一行已經(jīng)塞入標(biāo)題了*/ while (iterator.hasNext()){ Row row = sheet.createRow(index); /*取得當(dāng)前這行的map,該map中以key,value的形式存著這一行值*/ Map<String, String> map = (Map<String, String>)iterator.next(); /*循環(huán)列數(shù),給當(dāng)前行塞值*/ for(int i = 0; i<title.length; i++){ /*old存的是上一行統(tǒng)一位置的單元的值,第一行是最上一行了,所以從第二行開(kāi)始記*/ old = poiModels.get(i)==null?"":poiModels.get(i).getContent(); for(int j = 0; j < mergeIndex.length; j++){ /*記錄第一行的開(kāi)始行和開(kāi)始列*/ PoiModel poiModel = new PoiModel(); poiModel.setOldContent(map.get(title[i])); poiModel.setContent(map.get(title[i])); poiModel.setCellIndex(i); }else if(i > 0 && mergeIndex[j] == i){/*這邊i>0也是因?yàn)榈谝涣幸呀?jīng)是最前一列了,只能從第二列開(kāi)始*/ /*當(dāng)前同一列的內(nèi)容與上一行同一列不同時(shí),把那以上的合并, 或者在當(dāng)前元素一樣的情況下,前一列的元素并不一樣,這種情況也合并*/ /*如果不需要考慮當(dāng)前行與上一行內(nèi)容相同,但是它們的前一列內(nèi)容不一樣則不合并的情況,把下面條件中||poiModels.get(i).getContent().equals(map.get(title[i])) && !poiModels.get(i - 1).getOldContent().equals(map.get(title[i-1]))去掉就行*/ if(!poiModels.get(i).getContent().equals(map.get(title[i])) || poiModels.get(i).getContent().equals(map.get(title[i])) && !poiModels.get(i - 1).getOldContent().equals(map.get(title[i-1]))){ /*當(dāng)前行的當(dāng)前列與上一行的當(dāng)前列的內(nèi)容不一致時(shí),則把當(dāng)前行以上的合并*/ CellRangeAddress cra=new CellRangeAddress(poiModels.get(i).getRowIndex()/*從第二行開(kāi)始*/, index - 1/*到第幾行*/, poiModels.get(i).getCellIndex()/*從某一列開(kāi)始*/, poiModels.get(i).getCellIndex()/*到第幾列*/); sheet.addMergedRegion(cra); /*重新記錄該列的內(nèi)容為當(dāng)前內(nèi)容,行標(biāo)記改為當(dāng)前行標(biāo)記,列標(biāo)記則為當(dāng)前列*/ poiModels.get(i).setContent(map.get(title[i])); poiModels.get(i).setRowIndex(index); poiModels.get(i).setCellIndex(i); if(mergeIndex[j] == i && i == 0 && !poiModels.get(i).getContent().equals(map.get(title[i]))){ /*當(dāng)前行的當(dāng)前列與上一行的當(dāng)前列的內(nèi)容不一致時(shí),則把當(dāng)前行以上的合并*/ CellRangeAddress cra=new CellRangeAddress(poiModels.get(i).getRowIndex()/*從第二行開(kāi)始*/, index - 1/*到第幾行*/, poiModels.get(i).getCellIndex()/*從某一列開(kāi)始*/, poiModels.get(i).getCellIndex()/*到第幾列*/); sheet.addMergedRegion(cra); /*重新記錄該列的內(nèi)容為當(dāng)前內(nèi)容,行標(biāo)記改為當(dāng)前行標(biāo)記*/ poiModels.get(i).setContent(map.get(title[i])); poiModels.get(i).setRowIndex(index); poiModels.get(i).setCellIndex(i); /*最后一行沒(méi)有后續(xù)的行與之比較,所有當(dāng)?shù)阶詈笠恍袝r(shí)則直接合并對(duì)應(yīng)列的相同內(nèi)容*/ if(mergeIndex[j] == i && index == list.size()){ CellRangeAddress cra=new CellRangeAddress(poiModels.get(i).getRowIndex()/*從第二行開(kāi)始*/, index/*到第幾行*/, poiModels.get(i).getCellIndex()/*從某一列開(kāi)始*/, poiModels.get(i).getCellIndex()/*到第幾列*/); sheet.addMergedRegion(cra); Cell cell = row.createCell(i, Cell.CELL_TYPE_STRING); cell.setCellValue(map.get(title[i])); /*在每一個(gè)單元格處理完成后,把這個(gè)單元格內(nèi)容設(shè)置為old內(nèi)容*/ poiModels.get(i).setOldContent(old); FileOutputStream out = null; String fileName = String.valueOf(new Date().getTime()/1000); tempFile = File.createTempFile(fileName, ".xlsx"); localPath = tempFile.getAbsolutePath(); out = new FileOutputStream(localPath); public static void main(String[] args) throws IOException{ /*此處標(biāo)題的數(shù)組則對(duì)應(yīng)excel的標(biāo)題*/ String[] title = {"id","標(biāo)題","描述","負(fù)責(zé)人","開(kāi)始時(shí)間"}; List<Map<String, String>> list = Lists.newArrayList(); /*這邊是制造一些數(shù)據(jù),注意每個(gè)list中map的key要和標(biāo)題數(shù)組中的元素一致*/ for(int i = 0; i<10; i++){ HashMap<String, String> map = com.google.common.collect.Maps.newHashMap(); map.put("標(biāo)題","mmmm"); map.put("標(biāo)題","aaaaa"); map.put("標(biāo)題","哈哈哈哈"); map.put("標(biāo)題","hhhhhhhh"); map.put("標(biāo)題","bbbb"); map.put("負(fù)責(zé)人","vvvvv"); map.put("開(kāi)始時(shí)間","2017-02-27 11:20:26"); Map<String/*此處的key為每個(gè)sheet的名稱,一個(gè)excel中可能有多個(gè)sheet頁(yè)*/, List<Map<String/*此處key對(duì)應(yīng)每一列的標(biāo)題*/, String>>/*該list為每個(gè)sheet頁(yè)的數(shù)據(jù)*/> map = Maps.newHashMap(); map.put("測(cè)試合并數(shù)據(jù)", list); System.out.println(createExcel(title, map, new int[]{0,1,2}/*此處數(shù)組為需要合并的列,可能有的需求是只需要某些列里面相同內(nèi)容合并*/));
生成的文件效果(兩種情況的):


|