1. 概述

处理二维数组(2D数组)是Java开发中的常见操作,尤其在涉及矩阵运算的场景中。一个典型需求是计算二维数组对角线元素的和。

本教程将探讨计算二维数组主对角线和副对角线元素和的不同实现方案。

2. 问题定义

首先快速理解问题本质:

二维数组构成一个矩阵。我们假设矩阵是n×n的方阵,例如这个4×4的二维数组:

static final int[][] MATRIX = new int[][] {
    {  1,  2,  3,  4 },
    {  5,  6,  7,  8 },
    {  9, 10, 11, 12 },
    { 13, 14, 15, 100 }
};

明确两个关键对角线概念:

  • 主对角线:从左上到右下的对角线。上例中元素为1, 6, 11, 100
  • 副对角线:从右上到左下的对角线。上例中元素为4, 7, 10, 13

两条对角线的元素和如下:

static final int SUM_MAIN_DIAGONAL = 118; //1+6+11+100
static final int SUM_SECONDARY_DIAGONAL = 34; //4+7+10+13

为统一处理两种对角线类型,创建枚举:

enum DiagonalType {
    Main, Secondary
}

后续可通过传入DiagonalType获取对应结果

3. 识别对角线元素

计算对角线元素和的前提是准确定位对角线元素

  • 主对角线:当元素行索引(rowIdx)等于列索引(colIdx)时,该元素位于主对角线。如MATRIX[0][0]=1, MATRIX[1][1]=6
  • 副对角线:在n×n矩阵中,满足rowIdx + colIdx = n - 1。例如4×4矩阵中:
    • MATRIX[0][3]=4 (0+3=4-1)
    • MATRIX[1][2]=7 (1+2=4-1)
    • MATRIX[3][0]=13 (3+0=4-1)
    colIdx = n - rowIdx - 1

掌握定位规则后,开始实现求和方法。

4. 循环遍历方案

最直接的方式是根据对角线类型遍历行索引并累加元素

int diagonalSumBySingleLoop(int[][] matrix, DiagonalType diagonalType) {
    int sum = 0;
    int n = matrix.length;
    for (int rowIdx = 0; rowIdx < n; row++) {
        int colIdx = diagonalType == Main ? rowIdx : n - rowIdx - 1;
        sum += matrix[rowIdx][colIdx];
    }
    return sum;
}

核心逻辑:

  1. 根据对角线类型计算目标列索引
  2. 累加matrix[rowIdx][colIdx]到sum

验证结果:

assertEquals(SUM_MAIN_DIAGONAL, diagonalSumBySingleLoop(MATRIX, Main));
assertEquals(SUM_SECONDARY_DIAGONAL, diagonalSumBySingleLoop(MATRIX, Secondary));

✅ 两种对角线均得到正确结果

5. 使用IntBinaryOperator优化枚举

循环方案虽简单,但每次循环都要检查对角线类型。可通过为枚举添加IntBinaryOperator消除重复判断

enum DiagonalType {
    Main((rowIdx, len) -> rowIdx),
    Secondary((rowIdx, len) -> (len - rowIdx - 1));
    
    public final IntBinaryOperator colIdxOp;
    
    DiagonalType(IntBinaryOperator colIdxOp) {
        this.colIdxOp = colIdxOp;
    }
}

关键改进:

  • 为枚举添加IntBinaryOperator属性(函数式接口,接收两个int参数返回int结果
  • 使用Lambda表达式实现不同对角线的列索引计算逻辑

优化后的求和方法:

int diagonalSumFunctional(int[][] matrix, DiagonalType diagonalType) {
    int sum = 0;
    int n = matrix.length;
    for (int rowIdx = 0; rowIdx < n; row++) {
        sum += matrix[rowIdx][diagonalType.colIdxOp.applyAsInt(rowIdx, n)];
    }
    return sum;
}

直接调用applyAsInt()获取列索引,消除循环内的条件判断。测试同样通过:

assertEquals(SUM_MAIN_DIAGONAL, diagonalSumFunctional(MATRIX, Main));
assertEquals(SUM_SECONDARY_DIAGONAL, diagonalSumFunctional(MATRIX, Secondary));

6. Stream API方案

结合Java 8的Stream API和函数式特性,实现更简洁的方案:

public int diagonalSumFunctionalByStream(int[][] matrix, DiagonalType diagonalType) {
    int n = matrix.length;
    return IntStream.range(0, n)
      .map(i -> MATRIX[i][diagonalType.colIdxOp.applyAsInt(i, n)])
      .sum();
}

核心步骤:

  1. IntStream.range(0, n)生成行索引流
  2. map()将索引转换为对角线元素
  3. sum()完成求和

测试验证:

assertEquals(SUM_MAIN_DIAGONAL, diagonalSumFunctionalByStream(MATRIX, Main));
assertEquals(SUM_SECONDARY_DIAGONAL, diagonalSumFunctionalByStream(MATRIX, Secondary));

✅ 该方案代码更流畅易读,适合熟悉Stream API的开发者。

7. 总结

本文探讨了计算Java二维数组对角线元素和的多种实现方案。掌握主副对角线的索引规律是解决问题的关键

方案 优点 缺点
循环遍历 直观简单 循环内重复类型判断
枚举优化 消除条件判断 需理解函数式接口
Stream API 代码简洁 需Java 8+支持

完整源码可在GitHub获取。