使用Apache POI在Java中实现Excel单元格的合并

 更新时间:2025年03月05日 15:04:02   作者:好奇的菜鸟  
在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用 Apache POI 库在 Java 中实现 Excel 单元格的合并,需要的可以了解下

在日常工作中,Excel 是一个不可或缺的工具,尤其是在处理大量数据时。为了提升数据的可读性和美观性,我们经常需要对 Excel 中的单元格进行合并操作。本文将介绍如何使用 Apache POI 库在 Java 中实现 Excel 单元格的合并,并提供一个现成的工具类供大家使用。

工具类介绍

我们提供了一个名为 ExcelMergeUtility 的工具类,它可以帮助你轻松地合并 Excel 中的单元格。该类支持纵向合并(按行合并)和横向合并(按列合并),并且可以指定需要合并的列。

工具类代码

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.stereotype.Component;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

@Component
public class ExcelMergeUtility {

    /**
     * 合并Excel中内容相同的单元格
     *
     * @param workbook       工作簿
     * @param sheetName      工作表名称
     * @param columnsToMerge 需要合并的列索引列表
     * @param isRowMerge     是否按行合并(纵向合并)
     * @param isColumnMerge  是否按列合并(横向合并)
     */
    public void mergeCells(Workbook workbook, String sheetName, List<Integer> columnsToMerge,
                           boolean isRowMerge, boolean isColumnMerge) {
        Sheet sheet = workbook.getSheet(sheetName);
        if (sheet == null) {
            throw new IllegalArgumentException("工作表 " + sheetName + " 不存在");
        }

        // 记录第一列的合并行数
        int firstColMergeEndRow = 0;

        for (int colIndex : columnsToMerge) {
            for (int rowIndex = 0; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row currentRow = sheet.getRow(rowIndex);
                if (currentRow == null) continue;

                Cell currentCell = currentRow.getCell(colIndex);
                if (currentCell == null) continue;

                String currentValue = currentCell.getStringCellValue();

                if (isRowMerge) {
                    // 纵向合并
                    int mergeStartRow = rowIndex;
                    while (rowIndex + 1 <= sheet.getLastRowNum()) {
                        Row nextRow = sheet.getRow(rowIndex + 1);
                        if (nextRow == null) break;

                        Cell nextCell = nextRow.getCell(colIndex);
                        if (nextCell == null || !nextCell.getStringCellValue().equals(currentValue)) break;

                        // 如果当前列不是第一列,且合并行数超过前一列的合并行数,则停止合并
                        if (colIndex > 0 && rowIndex + 1 > getMergeEndRow(sheet, mergeStartRow, colIndex - 1)) break;

                        // 如果合并行数超过第一列的合并行数,则停止合并
                        if (colIndex > 0 && rowIndex + 1 > firstColMergeEndRow) break;

                        rowIndex++;
                    }
                    if (mergeStartRow != rowIndex) {
                        sheet.addMergedRegion(new CellRangeAddress(mergeStartRow, rowIndex, colIndex, colIndex));
                    }

                    // 如果是第一列,记录合并的最后一行
                    if (colIndex == 0) {
                        firstColMergeEndRow = rowIndex;
                    }
                }

                if (isColumnMerge) {
                    // 横向合并
                    int mergeStartCol = colIndex;
                    while (colIndex + 1 < currentRow.getLastCellNum()) {
                        Cell nextCell = currentRow.getCell(colIndex + 1);
                        if (nextCell == null || !nextCell.getStringCellValue().equals(currentValue)) break;

                        colIndex++;
                    }
                    if (mergeStartCol != colIndex) {
                        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, mergeStartCol, colIndex));
                    }
                }
            }
        }
    }

    /**
     * 获取指定单元格的合并的最后一行
     *
     * @param sheet     工作表
     * @param rowIndex  行索引
     * @param colIndex  列索引
     * @return 合并的最后一行
     */
      private int getMergeEndRow(Sheet sheet, int rowIndex, int colIndex) {
        int numMergedRegions = sheet.getNumMergedRegions();
        for (int i = 0; i < numMergedRegions; i++) {
            CellRangeAddress mergedRegion = sheet.getMergedRegion(i);
            if (mergedRegion.isInRange(rowIndex, colIndex)) {
                return mergedRegion.getLastRow();
            }
        }
//        for (CellRangeAddress mergedRegion : sheet.getMergedRegions()) {
//            if (mergedRegion.isInRange(rowIndex, colIndex)) {
//                return mergedRegion.getLastRow();
//            }
//        }
        return rowIndex; // 如果没有合并,则返回当前行
    }

    /**
     * 示例:生成Excel并合并单元格
     */
    public void generateAndMergeExcel(String filePath, String sheetName, List<Integer> columnsToMerge,
                                      boolean isRowMerge, boolean isColumnMerge) throws IOException {
        // 打开现有的Excel文件
        Workbook workbook = WorkbookFactory.create(new FileInputStream(filePath));

        // 合并单元格
        mergeCells(workbook, sheetName, columnsToMerge, isRowMerge, isColumnMerge);

        // 写入文件
        try (FileOutputStream fileOut = new FileOutputStream(filePath)) {
            workbook.write(fileOut);
        }

        workbook.close();
    }
}

调用示例

List<Integer> columnsToMerge = Arrays.asList(0, 1, 2, 3, 4, 5); // 合并第1列和第2列
boolean isRowMerge = true; // 启用纵向合并
boolean isColumnMerge = false; // 禁用横向合并

excelMergeUtility.generateAndMergeExcel("C:\\Users\\xxxx\\Downloads\\汇总记录2025-03-04%2B10_38_30.xls", "汇总", columnsToMerge, isRowMerge, isColumnMerge);

依赖配置

为了使用这个工具类,你需要在你的项目中添加 Apache POI 的依赖:

<!-- Apache POI 核心库 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<!-- Apache POI OOXML 库,用于处理 .xlsx 文件 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

总结

通过 ExcelMergeUtility 工具类,你可以轻松地实现 Excel 单元格的合并操作。无论是纵向合并还是横向合并,该工具类都能满足你的需求。

到此这篇关于使用Apache POI在Java中实现Excel单元格的合并的文章就介绍到这了,更多相关Apache POI合并Excel单元格内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • BigDecimal的toString()、toPlainString()和toEngineeringString()区别及用法详解

    BigDecimal的toString()、toPlainString()和toEngineeringString()区

    使用BigDecimal进行打印的时候,经常会对BigDecimal提供的三个toString方法感到好奇,以下整理3个toString方法的区别及用法,需要的朋友可以参考下
    2023-08-08
  • springboot日期转换器实现实例解析

    springboot日期转换器实现实例解析

    这篇文章主要介绍了springboot日期转换器实现实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 解决RestTemplate 请求url中包含百分号 会被转义成25的问题

    解决RestTemplate 请求url中包含百分号 会被转义成25的问题

    这篇文章主要介绍了解决RestTemplate 请求url中包含百分号 会被转义成25的问题,具有很好的参考价值,希望对大家有所帮助。
    2021-10-10
  • Java中List集合去重的几种方式详细解析

    Java中List集合去重的几种方式详细解析

    这篇文章主要介绍了Java中List集合去重的几种方式详细解析,在日常的业务开发中,偶尔会遇到需要将 List 集合中的重复数据去除掉的场景,那么今天我们来看看几种LIst集合去重的方式,需要的朋友可以参考下
    2023-11-11
  • Java获取随机数的3种方法

    Java获取随机数的3种方法

    本篇文章主要介绍了Java获取随机数的3种方法,现在分享给大家,也给大家做个参考,感兴趣的小伙伴们可以参考一下。
    2016-11-11
  • 散列表的原理与Java实现方法详解

    散列表的原理与Java实现方法详解

    这篇文章主要介绍了散列表的原理与Java实现方法,详细分析了散列表的原理,并结合实例形式分析了java实现散列表相关操作技巧,需要的朋友可以参考下
    2019-09-09
  • Spring Boot Yaml配置高级用法

    Spring Boot Yaml配置高级用法

    这篇文章主要介绍了Spring Boot Yaml配置高级用法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Java并发编程:volatile关键字详细解析

    Java并发编程:volatile关键字详细解析

    这篇文章主要介绍了Java并发编程:volatile关键字详细解析,对学习volatile关键字有一定的认识,有需要的可以了解一下。
    2016-11-11
  • Java对数器验证算法详解

    Java对数器验证算法详解

    这篇文章主要介绍了Java对数器验证算法,Java对数函数的计算方法非常有问题,然而在API中却有惊人的误差。但是假如运用了以下的方法,用Java处理数字所碰到的小麻烦就可以轻而易举的解决了
    2023-04-04
  • Java  匿名内部类详解及实例代码

    Java 匿名内部类详解及实例代码

    这篇文章主要介绍了Java 匿名内部类详解及实例代码的相关资料,需要的朋友可以参考下
    2017-02-02

最新评论