核心思路是绕过泛型限制,用原始类型专用接口(如IntPredicate、IntFunction)替代通用接口,从签名层面杜绝装箱拆箱;优先选用Java提供的原始类型函数式接口,配合IntStream等原始流、避免List<Integer>等装箱集合,复用Lambda实例以减少对象创建。

核心思路是绕过泛型限制,用原始类型专用接口替代通用接口,从签名层面杜绝装箱拆箱。
优先选用原始类型特化接口
Java 提供了 IntPredicate、IntFunction、IntConsumer 等原始类型函数式接口,它们的方法参数和返回值直接使用 int 而非 Integer。例如:
-
IntPredicate:
boolean test(int value)—— 输入是栈上原始 int,无需拆箱 -
ToIntFunction<T>:
int applyAsInt(T value)—— 返回 int,不生成 Integer 对象 - 同理,LongPredicate、DoubleConsumer、ToLongBiFunction 等也遵循相同设计
只要业务逻辑不依赖 null 或泛型通配,就应直接选用这些接口,而非 Predicate<Integer> 或 Function<String, Integer>。
Stream 处理数值时全程保持原始类型
避免先用 Stream<Integer> 再 map 转换,而要从源头启用原始类型流:
立即学习“Java免费学习笔记(深入)”;
- 用
IntStream.range(0, 1000)替代Stream.iterate(0, i -> i + 1).limit(1000) - 对已有 int 数组,用
Arrays.stream(intArray)(返回 IntStream),而非Arrays.asList(intArray).stream()(会装箱) - 链式操作中,用
mapToInt、filter(IntPredicate)、sum()等原生方法,全程不触碰堆对象
Lambda 定义与复用需注意捕获行为
即使用了 IntPredicate,若在循环内反复定义 Lambda,仍可能因闭包捕获产生额外对象:
- 错误:在 for 循环里写
IntPredicate p = x -> x > threshold;—— 每次迭代新建实例 - 正确:提前定义好并复用,如
static final IntPredicate IS_POSITIVE = x -> x > 0; - 若 threshold 是变量,可封装为方法返回 IntPredicate,避免每次 new 实例
集合与工具类配合原始类型容器
函数式接口常用于集合处理,但 List<Integer> 本身已是装箱源头:
- 高频场景下,改用 Eclipse Collections 的
IntArrayList或 Trove 的TIntArrayList - Android 开发可用
SparseArray<String>,键为原始 int,零装箱 - 避免
Arrays.asList(1, 2, 3),它返回的是装箱后的List<Integer>



















