关键在于避开自动装箱拆箱,直接用intconsumer、longsupplier等原始类型签名的专用接口——它们通过jvm栈上值传递实现零对象开销,而consumer等泛型接口每次调用都会触发valueof()装箱或xxxvalue()拆箱。

Java 中函数式接口处理基本类型,关键在于**避开自动装箱拆箱**,直接用原始类型签名的专用接口——比如 IntConsumer、LongSupplier、DoublePredicate 这类。它们不是语法糖,而是 JVM 层面支持的“零包装”通道。
为什么不能只用 Consumer?
因为 ConsumerInteger.valueOf() 装箱;而 IntConsumer 的 accept(int value) 方法签名明确声明接收原始 int,值直接在栈上传递,不创建任何对象。
- 普通接口(如
Function<integer string></integer>)→ 每次调用都触发装箱/拆箱 - 专用接口(如
IntFunction<string></string>)→ 入参出参全是int,全程无对象开销 - 所有专用接口都带
@FunctionalInterface注解,完全兼容 Lambda 和方法引用
常见基本类型专用接口速查
命名有规律:前缀表示类型(Int/Long/Double),后缀表示行为(Consumer/Supplier/Predicate/Function/Operator):
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
IntConsumer:
void accept(int),适合遍历、打印、记录等无返回消费操作 -
LongSupplier:
long getAsLong(),适合生成随机数、时间戳等无参返 long 场景 -
DoublePredicate:
boolean test(double),用于判断 double 值是否满足条件 -
IntBinaryOperator:
int applyAsInt(int, int),两个 int 输入,一个 int 输出,如加减乘除
实际写法对比:哪里容易踩坑?
看三类高频场景,左边是“隐式装箱”的写法,右边是“原始类型直达”的优化写法:
-
遍历 int 数组:
❌Arrays.stream(nums).boxed().forEach(System.out::println)(先转成Stream<integer></integer>,再装箱)
✅Arrays.stream(nums).forEach(System.out::println)(nums是int[],底层为IntStream,自动匹配IntConsumer) -
获取当前纳秒时间:
❌Supplier<long> s = () -> System.nanoTime()</long>(每次返回新Long对象)
✅LongSupplier s = System::nanoTime(直接返回原始long,无构造开销) -
过滤正整数列表:
❌list.stream().filter(i -> i > 0).collect(...)(list是List<integer></integer>,每个i都要拆箱)
✅list.stream().mapToInt(Integer::intValue).filter(i -> i > 0).toArray()(转为原始流,全程int运算)
组合与复用:还能链式操作吗?
能,而且更干净。这些专用接口自带默认方法,组合逻辑不引入任何包装对象:
-
IntPredicate isEven = i -> i % 2 == 0;IntPredicate isPositive = i -> i > 0;IntPredicate both = isEven.and(isPositive);(返回仍是IntPredicate,仍只操作int) -
IntConsumer logAndSave = i -> { logger.info("Got: {}", i); db.save(i); };(可安全用于每秒万次回调,不会因重复调用产生大量Integer实例)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










