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 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.5 | ClassNotFoundException → 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.1 | 2016-03-21 最终版,断代约 10 年 | 统计+线性代数+复数/分数/素数一站式;零传递依赖;JDK 8/17 实测可运行 | 2016 年后无新特性;非新式 API 风格 | 默认选择:绝大多数 Java 数学统计需求 |
| math4 4.0-beta1 | 2022-12-19,beta 后停滞约 4 年;6 模块拆分 | 模块化、AccurateMath 精度路线、为后续演进铺路 | 只有 beta,官方自述 work-in-progress;旧 API 在 legacy 包 | 仅评估迁移时试用;生产慎用 |
| EJML | 独立线性代数库,持续维护 | 矩阵运算性能好、API 现代 | 只做线性代数,无统计推断/t 检验 | 矩阵密集型且性能敏感时替代 math3 的 linear 包 |
按场景落到具体建议:
- 新项目、通用数学统计 → math3 3.6.1(本文方式集成,10 分钟可用)。
- 只用矩阵运算且追求吞吐 → 评估 EJML;math3 的 LU/Eigen 在常规规模(数百维)足够。
- 规划长期演进、愿意跟进 beta → math3 与 math4 同 classpath 过渡(第四章实测),按模块逐步迁移到
org.apache.commons.math4.legacy.*,低层随机数/几何等改引 Commons RNG/Geometry 等拆分组件。 - 深度学习/张量场景 → math3 不覆盖,另选 ND4J 等专用库(对比见第 1 篇安装指南 3.3 节拓展表)。
六、参考链接
- Apache Commons Math 官方站点:https://commons.apache.org/proper/commons-math/
- Commons Math 3.6.1 Javadoc:https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/
- Maven 中央仓库坐标页:https://repo1.maven.org/maven2/org/apache/commons/commons-math3/
- 官方发行下载目录(离线包):https://downloads.apache.org/commons/math/
- EJML 官方站点:https://ejml.org/