Apache Commons Math 3.6.1 快速入门与集成示例

用 10 分钟在 Maven 工程中引入 Apache Commons Math 3.6.1 并跑通第一个统计程序,覆盖依赖引入与 compiler 插件固定、No sources to compile 等常见坑、math3 与 math4 同 classpath 共存的实测验证,并给出与 EJML/math4 的选型建议,全部已在 Ubuntu 22.04 实测。

Apache Commons MathApache Commons Math2026/10/15

Apache Commons Math 3.6.1 快速入门与集成示例

目标:10 分钟内在 Maven 工程中引入 Apache Commons Math 3.6.1,跑通第一个统计程序;掌握 Maven 集成的高频坑;验证 math3 与 math4 在同一 classpath 的共存能力;最后给出选型决策建议。以下全部命令与回显均已在 Ubuntu 22.04(x86_64)、OpenJDK 17.0.20.1 实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • JDK:OpenJDK 17.0.20.1(java -version 实测)
  • 构建工具:Apache Maven 3.6.3
  • 目标库:Apache Commons Math 3.6.1(org.apache.commons:commons-math3:3.6.1)
  • 版本事实:3.6.1 于 2016-03-21 发布(lastUpdated=20160321125613),此后断代约 10 年无更新,但它是 Maven Central 上 latest/release 的最终版本,且零传递依赖、API 稳定,生产可放心使用;新一代 commons-math4 只有 4.0-beta1(2022-12-19)。
  • Maven 示例工程目录:/opt/math-demo

确认 JDK 与 Maven 版本基线:

java -version 2>&1
mvn -v | head -3

输出:

openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64

⚠️ 注意:JDK 8 及以上即可使用(math3 是 major=49 的 Java 5 字节码);本文示例按 release 17 编译。没有 Maven 或无法联网的离线环境,改用官方二进制包方式,见第 1 篇《Ubuntu 22.04 安装 Apache Commons Math 指南》第五章。

二、10 分钟最小可用

本节从空目录到跑通第一个程序共 4 步,全程约 10 分钟:建工程(1 分钟)→ 写 pom(2 分钟)→ 写程序(2 分钟)→ 编译运行验证(5 分钟,首次下载依赖视网速)。

2.1 创建工程并引入依赖

创建工程目录:

mkdir -p /opt/math-demo/src/main/java/demo

/opt/math-demo/pom.xml 全文(最小可用配置,直接照抄):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>demo</groupId>
  <artifactId>math-demo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.release>17</maven.compiler.release>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-math3</artifactId>
      <version>3.6.1</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.11.0</version>
      </plugin>
    </plugins>
  </build>
</project>

两个必做项,缺一会在 2.3 步踩坑:

  • 显式固定 maven-compiler-plugin 为 3.11.0:Maven 3.6.3 默认绑定的 compiler 插件是 3.1,不认识 maven.compiler.release 属性,会按 source/target=1.5 编译并被 JDK 17 拒绝(报 Source option 5 is no longer supported,排障见 3.2 节)。
  • 声明 project.build.sourceEncoding=UTF-8:源码含中文注释/字符串时防止按 POSIX 默认编码编译报 unmappable character(见 3.3 节)。

依赖坐标说明:groupId 固定 org.apache.commons、artifactId commons-math3、版本 3.6.1。该库零传递依赖(第三章依赖树实测验证),声明这一项即引入全部能力。

2.2 编写第一个程序

/opt/math-demo/src/main/java/MathCheck.java 全文(默认包,直接复制):

import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

public class MathCheck {
    public static void main(String[] args) {
        DescriptiveStatistics ds = new DescriptiveStatistics();
        for (double v : new double[] {9, 2, 5, 4, 12, 7, 8, 11, 9, 3}) {
            ds.addValue(v);
        }
        System.out.println("N=" + ds.getN()
            + " mean=" + ds.getMean()
            + " sd=" + ds.getStandardDeviation()
            + " p50=" + ds.getPercentile(50));
        System.out.println("commons-math3 OK");
    }
}

程序用 DescriptiveStatistics 对 10 个测量值计算样本数、均值、标准差与中位数——这是 commons-math3 最常用的统计入口。源码文件必须放在 src/main/java/ 约定目录(默认包类直接放该目录下),否则触发 3.1 节的假成功坑。

2.3 编译与运行

编译:

cd /opt/math-demo && mvn -q compile
echo "EXIT=$?"

输出:

EXIT=0

查看构建细节与依赖树(确认零传递依赖):

mvn clean compile 2>&1 | tail -14

输出:

[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ math-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/math-demo/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ math-demo ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 1 source file with javac [debug release 17] to target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  6.876 s
[INFO] Finished at:  2026-10-14T15:29:51Z
mvn dependency:tree 2>&1 | grep -A 8 "maven-dependency-plugin:2.8:tree" | head -14

输出:

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ math-demo ---
[INFO] demo:math-demo:jar:1.0.0
[INFO] \- org.apache.commons:commons-math3:jar:3.6.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.777 s
[INFO] Finished at: 2026-10-14T15:15:02Z
[INFO] ------------------------------------------------------------------------

解读:依赖树中除工程自身外只有 commons-math3:3.6.1 一个节点,零传递依赖,不会拖入任何其他 jar,不会产生版本冲突。编译日志出现 Compiling 1 source file with javac [debug release 17],插件固定生效。

运行(classpath = 编译输出 + 本地仓库的 math3 jar):

M3=~/.m2/repository/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
java -Dfile.encoding=UTF-8 -cp target/classes:$M3 MathCheck

输出:

N=10 mean=7.0 sd=3.39934634239519 p50=7.5
commons-math3 OK

2.4 验证点与预期结果

拿到以下结果即集成成功,任一不符按第三章排查:

检查项预期(实测值)不符时排查
mvn -q compile 退出码EXIT=0报 Source option 5 → 3.2 节;报 unmappable character → 3.3 节
编译日志Compiling 1 source file with javac [debug release 17]显示 No sources to compile → 3.1 节
运行输出第一行N=10 mean=7.0 sd=3.39934634239519 p50=7.5ClassNotFoundException → 3.1 节
运行输出第二行commons-math3 OK中文变问号 → 3.3 节

手算核对:10 个数总和 70 → 均值 7.0;排序后第 5、6 个数是 7 和 8 → 中位数 7.5,与实测一致。

三、Maven 工程常见坑

以下三个坑在 commons-math3 集成中出现频率最高,均为实测复现,给出定位与修复步骤。

3.1 坑一:No sources to compile 假成功

现象:mvn compile 显示 BUILD SUCCESS,但运行时报 ClassNotFoundException: MathCheck,编译日志里只有 No sources to compile、没有 Compiling N source file。复现方式:把 MathCheck.java 放在工程根目录(而不是 src/main/java/)。

定位:Maven 按约定目录找源码(src/main/java/),找不到源码时 compiler 插件不报错而是跳过,于是"成功"了一个什么都没编译的构建——target/classes 里没有 class。修复:把源码移到约定目录后重新编译,日志出现 Compiling 1 source file 才是真成功(默认包类放 src/main/java/MathCheck.java;带包名的类按包路径建子目录,如 demo.MathDemo 放 src/main/java/demo/MathDemo.java)。

3.2 坑二:老 compiler 插件不认识 release 参数

现象:mvn compile 报错(同时含两行):

[ERROR] Source option 5 is no longer supported. Use 7 or later.
[ERROR] Target option 5 is no longer supported. Use 7 or later.

定位:Maven 3.6.3 默认绑定的 maven-compiler-plugin 版本是 3.1,它不认识 pom 里的 maven.compiler.release 属性,退回按 source/target=1.5 处理,被 JDK 17 的 javac 拒绝。修复:在 pom 的 <build><plugins> 中显式固定 3.11.0(见 2.1 节 pom 全文)。该坑在老版本 Maven(如 CentOS 8 的 3.5.4)上同样存在,修复方式相同。

3.3 坑三:编码与 locale

两个环节都可能踩:

  • 编译期:源码含中文而未声明编码时,javac 按平台默认编码(POSIX/C locale 下为 ANSI_X3.4-1968)解码,报 unmappable character for encoding ASCII。修复:pom 设 project.build.sourceEncoding=UTF-8(Maven 工程),或 javac 加 -encoding UTF-8(命令行编译)。
  • 运行期:POSIX/C locale 下 System.out 默认输出编码为 ASCII,中文变问号(数字与英文不受影响)。修复:运行时加 JVM 系统属性 -Dfile.encoding=UTF-8(见 2.3 节运行命令)。

四、与其他库共存

与其他库共存的两个典型问题:math3 与 math4 能否同 classpath?会不会类冲突?本节实测回答。

结论先行:math3 与 math4 可以同 classpath 共存,不会冲突。 原因是两代顶层包名不同——math3 在 org.apache.commons.math3.*,math4 在 org.apache.commons.math4.*(旧 API 迁入 org.apache.commons.math4.legacy.* 子包),Java 按全限定类名加载类,两个同名简单类(如两代的 DescriptiveStatistics)分属不同包,互不覆盖。

以下是同 classpath 共存的完整实测。在 /opt/math-demo/coexist-src/demo/CoexistDemo.java 写一个同时调用两代 API 的程序(同名类通过全限定名区分,import 只给 math3 一份,math4 一律写全限定名):

package demo;

import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

public class CoexistDemo {
    public static void main(String[] args) {
        double[] data = {9, 2, 5, 4, 12, 7, 8, 11, 9, 3};
        DescriptiveStatistics s3 = new DescriptiveStatistics();
        for (double v : data) {
            s3.addValue(v);
        }
        System.out.println("math3 class: " + s3.getClass().getName());
        System.out.println("math3 N=" + s3.getN() + " mean=" + s3.getMean());

        org.apache.commons.math4.legacy.stat.descriptive.DescriptiveStatistics s4 =
            new org.apache.commons.math4.legacy.stat.descriptive.DescriptiveStatistics();
        for (double v : data) {
            s4.addValue(v);
        }
        System.out.println("math4 class: " + s4.getClass().getName());
        System.out.println("math4 N=" + s4.getN() + " mean=" + s4.getMean());

        s4.removeMostRecentValue();
        System.out.println("math4 after removeMostRecentValue: N=" + s4.getN()
            + " mean=" + s4.getMean());
    }
}

math4 jar 可从 Maven Central 下载(或让任意 Maven 工程拉入本地仓库),与 math3 jar 一起编译运行:

cd /opt/math-demo
R=~/.m2/repository/org/apache/commons
M3=$R/commons-math3/3.6.1/commons-math3-3.6.1.jar
M4L=$R/commons-math4-legacy/4.0-beta1/commons-math4-legacy-4.0-beta1.jar
M4LC=$R/commons-math4-legacy-core/4.0-beta1/commons-math4-legacy-core-4.0-beta1.jar
M4LE=$R/commons-math4-legacy-exception/4.0-beta1/commons-math4-legacy-exception-4.0-beta1.jar
M4C=$R/commons-math4-core/4.0-beta1/commons-math4-core-4.0-beta1.jar
javac -encoding UTF-8 -cp $M3:$M4L:$M4LC:$M4LE:$M4C -d coexist-classes coexist-src/demo/CoexistDemo.java
java -Dfile.encoding=UTF-8 -cp coexist-classes:$M3:$M4L:$M4LC:$M4LE:$M4C demo.CoexistDemo

输出:

math3 class: org.apache.commons.math3.stat.descriptive.DescriptiveStatistics
math3 N=10 mean=7.0
math4 class: org.apache.commons.math4.legacy.stat.descriptive.DescriptiveStatistics
math4 N=10 mean=7.0
math4 after removeMostRecentValue: N=9 mean=7.444444444444445

验证点:两个 DescriptiveStatistics 类分别加载自 org.apache.commons.math3.stat.descriptive 与 org.apache.commons.math4.legacy.stat.descriptive,同一 classpath 各自正常工作、计算一致;math4 的 removeMostRecentValue() 移除最近观测(3)后 N=9、均值 67/9=7.444…,滚动语义正确。这套共存模式可用于迁移期渐进替换:新代码逐步改用 math4 包名,旧代码不动。

⚠️ 注意:math4 的 DescriptiveStatistics 位于 commons-math4-legacy 模块,编译运行时 classpath 需带上它的依赖模块(legacy-core、legacy-exception,必要时加 core),否则报 NoClassDefFoundError。另一个实测锚点:math4-legacy 4.0-beta1 中该方法名仍是 removeMostRecentValue()(javap 核对),网传"改名 removeMostRecentObservation"的说法与 4.0-beta1 的 jar 不符,写代码前用 javap -cp <jar> <全限定类名> 核对真实签名。

五、选型建议

三种主流方案的决策表(结合本文实测与版本事实):

方案版本事实优势局限适用判断
math3 3.6.12016-03-21 最终版,断代约 10 年统计+线性代数+复数/分数/素数一站式;零传递依赖;JDK 8/17 实测可运行2016 年后无新特性;非新式 API 风格默认选择:绝大多数 Java 数学统计需求
math4 4.0-beta12022-12-19,beta 后停滞约 4 年;6 模块拆分模块化、AccurateMath 精度路线、为后续演进铺路只有 beta,官方自述 work-in-progress;旧 API 在 legacy 包仅评估迁移时试用;生产慎用
EJML独立线性代数库,持续维护矩阵运算性能好、API 现代只做线性代数,无统计推断/t 检验矩阵密集型且性能敏感时替代 math3 的 linear 包

按场景落到具体建议:

  1. 新项目、通用数学统计 → math3 3.6.1(本文方式集成,10 分钟可用)。
  2. 只用矩阵运算且追求吞吐 → 评估 EJML;math3 的 LU/Eigen 在常规规模(数百维)足够。
  3. 规划长期演进、愿意跟进 beta → math3 与 math4 同 classpath 过渡(第四章实测),按模块逐步迁移到 org.apache.commons.math4.legacy.*,低层随机数/几何等改引 Commons RNG/Geometry 等拆分组件。
  4. 深度学习/张量场景 → math3 不覆盖,另选 ND4J 等专用库(对比见第 1 篇安装指南 3.3 节拓展表)。

六、参考链接

  1. Apache Commons Math 官方站点:https://commons.apache.org/proper/commons-math/
  2. Commons Math 3.6.1 Javadoc:https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/
  3. Maven 中央仓库坐标页:https://repo1.maven.org/maven2/org/apache/commons/commons-math3/
  4. 官方发行下载目录(离线包):https://downloads.apache.org/commons/math/
  5. EJML 官方站点:https://ejml.org/