← 返回题目列表

IntStream、LongStream 这些数值流是干什么的?为什么处理数字要用它们?

中等 第 23 / 24 题 更新于 2026/07/28
IntStream数值流装箱summaryStatistics

简化版

**IntStreamLongStreamDoubleStream 是「基本类型的特化流」——它们处理 int/long/double 时直接用基本类型,避免了普通 Stream<Integer> 的「装箱/拆箱」开销,性能更好,还提供了专门的数值方法(sumaveragerangesummaryStatistics 等)。**为什么需要它们:普通 Stream<Integer> 里的每个元素是包装类 Integer(对象),做数值运算要不断「拆箱成 int 算、再装箱成 Integer」,大量数据时装箱拆箱开销很大;数值流直接用 int,没有这个开销。常用能力① 直接求和/平均——sum()average()(普通 Stream 没有,要 reduce);② 生成范围——IntStream.range(0, 10)(09)、rangeClosed(1, 10)(110),替代传统 for 循环;③ 一次性统计——summaryStatistics() 返回 IntSummaryStatistics,一次拿到 count/sum/min/max/average;④ 互转——mapToInt(对象流转数值流)、boxed/mapToObj(数值流转对象流)。核心:处理数字用数值流(避免装箱、有专门数值方法),对象流和数值流之间用 mapToInt/boxed 互转。

详细版

三种数值流

元素类型
IntStreamint
LongStreamlong
DoubleStreamdouble

数值流的专属方法

方法作用
sum()求和(普通 Stream 没有)
average()平均(返回 OptionalDouble)
max()/min()最值(返回 OptionalInt)
range(a,b)/rangeClosed(a,b)生成范围([a,b) / [a,b])
summaryStatistics()一次拿 count/sum/min/max/average
mapToObj/boxed转成对象流
// 求和:数值流直接 sum(普通 Stream 要 reduce)
int total = list.stream().mapToInt(Order::getAmount).sum();

// 平均
OptionalDouble avg = list.stream().mapToInt(Order::getAmount).average();

// 生成范围(替代 for 循环)
IntStream.range(0, 5).forEach(System.out::println);       // 0 1 2 3 4
IntStream.rangeClosed(1, 5).forEach(System.out::println); // 1 2 3 4 5
int sum = IntStream.rangeClosed(1, 100).sum();            // 1+2+...+100 = 5050

// 一次拿所有统计值
IntSummaryStatistics stats = list.stream()
    .mapToInt(Order::getAmount)
    .summaryStatistics();
stats.getCount(); stats.getSum(); stats.getMin();
stats.getMax(); stats.getAverage();   // 一次遍历全拿到

// 互转:对象流 ↔ 数值流
IntStream is = list.stream().mapToInt(Order::getAmount);  // 对象流 → IntStream
Stream<Integer> boxed = is.boxed();                       // IntStream → Stream<Integer>
Stream<String> strs = IntStream.range(0, 3).mapToObj(i -> "item" + i); // → 对象流

⚠️ 数值流的核心价值是「避免装箱拆箱」——Stream<Integer> 里每个元素是 Integer 对象(在堆上),对它做数值运算(+、比较)时,JVM 要先「拆箱」把 Integer 转成 int 计算、有时还要「装箱」把结果转回 Integer大量数据时,这些装箱/拆箱 + 对象创建的开销累积起来很可观IntStream 直接用 int(栈上的基本类型),全程没有装箱/拆箱,也不创建 Integer 对象,所以处理数字更快、更省内存。所以当你要对一堆数字求和、求平均、统计时,用 mapToInt 转成 IntStream 再操作list.stream().mapToInt(...).sum()),而不是 list.stream().map(...).reduce(0, Integer::sum)(后者一路装箱)。另外数值流有专门的 sum()/average(),普通 Stream 没有(要用 reduce/collect,更繁琐)。

完整版教学

一、问题:Stream 的装箱开销

先理解普通对象流处理数字的问题:

Stream<Integer> 处理数字的问题——装箱/拆箱:
  Stream<Integer> 里每个元素是 Integer(包装类、堆上的对象)

  做数值运算时:
    Integer a, b;
    a + b  → 先把 a、b 拆箱成 int → int 相加 → 结果可能装箱回 Integer
    → 每次运算都有"拆箱(Integer→int)"、有时"装箱(int→Integer)"

  大量数据时:
    100 万个 Integer 求和:
    → 100 万次拆箱 + 中间结果装箱
    → 装箱/拆箱开销 + Integer 对象占内存 + GC 压力
    → 慢、费内存

  Integer 对象本身也占空间:
    int 是 4 字节;Integer 是一个对象(对象头 + int 值,十几字节)
    → 100 万个 Integer 比 100 万个 int 占多得多的内存

所以:用 Stream<Integer> 处理大量数字,装箱拆箱是性能负担
  → 需要"直接用 int"的流 → IntStream

普通 Stream<Integer> 处理数字的问题是「装箱/拆箱开销」——每个元素是 Integer(堆上对象),数值运算要拆箱成 int 算、有时装箱回 Integer;大量数据时装箱/拆箱开销 + Integer 对象占内存 + GC 压力(100 万个 Integer 比 100 万个 int 占多得多内存,int 4 字节 vs Integer 对象十几字节)。所以需要「直接用 int」的流——IntStream。理解「Stream处理数字有装箱拆箱开销(每次运算拆箱、Integer 对象占内存+GC 压力)、大量数据时是性能负担、需要直接用 int 的流」,就理解了数值流要解决的问题。

二、IntStream:直接用 int

IntStream 直接用基本类型 int,没有装箱:

IntStream(还有 LongStream/DoubleStream):
  元素直接是 int(基本类型,栈上),不是 Integer 对象
  → 全程没有装箱/拆箱
  → 不创建 Integer 对象(省内存、省 GC)
  → 数值运算直接用 int(快)

对比:
  Stream<Integer>:元素是 Integer 对象,运算要装箱拆箱
  IntStream:元素是 int,运算直接算

所以处理数字,IntStream 比 Stream<Integer> 快、省内存

三种数值流:
  IntStream:int
  LongStream:long
  DoubleStream:double
  (没有 ByteStream/ShortStream,用 IntStream 覆盖)

IntStream(及 LongStream/DoubleStream直接用基本类型(元素是 int 不是 Integer 对象)——全程无装箱/拆箱、不创建对象、运算直接用 int(快、省内存、省 GC)。对比 Stream<Integer>(元素是对象、运算要装箱拆箱),处理数字 IntStream 更快更省内存。三种:IntStream(int)/LongStream(long)/DoubleStream(double)(没有 Byte/Short 流,用 IntStream 覆盖)。理解「IntStream 直接用 int(不是 Integer 对象、全程无装箱拆箱、不创建对象、运算快省内存)、比 Stream好、三种数值流 Int/Long/Double」,就掌握了数值流的核心优势。

三、专属数值方法:sum、average

数值流有专门的数值方法,普通 Stream 没有:

数值流专属方法(普通 Stream<Integer> 没有):
  sum():求和 → 直接返回 int/long/double
    IntStream.of(1,2,3).sum()  → 6
  average():平均 → OptionalDouble(可能是空流)
  max()/min():最值 → OptionalInt
  count():元素个数

对比普通 Stream 求和(繁琐):
  Stream<Integer> 求和:
    stream.reduce(0, Integer::sum)  // 要用 reduce,且装箱
    或 stream.collect(summingInt(...))  // 收集器
  → 没有直接的 sum()

  IntStream 求和:
    intStream.sum()  // 直接,无装箱
  → 简洁 + 高效

所以:需要对数字求和/平均/最值,用数值流的专属方法
  比普通 Stream 的 reduce/collect 简洁且高效

数值流有专属数值方法(普通 Stream<Integer> 没有):sum()(求和,直接返回 int)、average()(平均,返回 OptionalDouble)、max()/min()(最值,OptionalInt)、count()。对比普通 Stream 求和要用 reduce(0, Integer::sum)collect(summingInt)(繁琐且装箱),IntStream 直接 sum()(简洁 + 无装箱)。理解「数值流专属方法 sum()/average()/max()/min()(普通 Stream 没有)、比 reduce/collect 简洁高效、直接对数字求和平均用它们」,就掌握了数值流的专属方法。

四、range:生成数字范围

range/rangeClosed 生成数字范围,替代传统 for 循环:

IntStream.range(a, b):生成 [a, b) 的整数流(不含 b)
  IntStream.range(0, 5)  → 0, 1, 2, 3, 4

IntStream.rangeClosed(a, b):生成 [a, b] 的整数流(含 b)
  IntStream.rangeClosed(1, 5)  → 1, 2, 3, 4, 5

替代传统 for 循环:
  传统:for (int i = 0; i < 5; i++) { ... }
  数值流:IntStream.range(0, 5).forEach(i -> { ... })

配合其他操作:
  IntStream.rangeClosed(1, 100).sum()          // 1+2+...+100 = 5050
  IntStream.rangeClosed(1, 10).filter(i -> i % 2 == 0).sum()  // 偶数和
  IntStream.range(0, arr.length)
      .mapToObj(i -> arr[i] + ":" + i)          // 带索引处理数组

其他生成方式:
  IntStream.of(1, 2, 3)         // 指定元素
  IntStream.iterate(1, n -> n*2).limit(10)  // 迭代生成
  IntStream.generate(...)       // 生成器
  Arrays.stream(intArray)       // 从 int[] 数组

所以 range 让"数字范围的遍历/处理"函数式化

range/rangeClosed 生成数字范围:range(a, b) 生成 [a, b)(不含 b)、rangeClosed(a, b) 生成 [a, b](含 b)。替代传统 for 循环for(i=0;i<5;i++)IntStream.range(0,5).forEach(...))。配合操作:rangeClosed(1,100).sum()(1~100 求和 5050)、带索引处理数组。其他生成:IntStream.ofiterateArrays.stream(intArray)。理解「range(a,b)生成[a,b)、rangeClosed(a,b)生成[a,b]、替代 for 循环、配合 sum/filter、Arrays.stream(intArray)从数组」,就掌握了 range 生成。

五、summaryStatistics:一次拿所有统计

summaryStatistics() 一次性拿到所有统计值:

summaryStatistics():返回 IntSummaryStatistics(一个统计对象)
  一次遍历,拿到 count、sum、min、max、average 全部:
    IntSummaryStatistics stats = stream.mapToInt(...).summaryStatistics();
    stats.getCount()    元素个数
    stats.getSum()      总和
    stats.getMin()      最小
    stats.getMax()      最大
    stats.getAverage()  平均

为什么用它(一次遍历,省事):
  如果分别求 count、sum、min、max、average:
    要遍历 5 次(每个统计一次)→ 浪费
  用 summaryStatistics:一次遍历全算出来 → 高效

配合 groupingBy(分组统计):
  groupingBy(Order::getType,
    Collectors.summarizingInt(Order::getAmount))
  → Map<类型, IntSummaryStatistics>
  → 每种类型的 count/sum/min/max/average 一次算出

所以需要多个统计值时,summaryStatistics 一次搞定,别分别求

summaryStatistics() 一次遍历拿到所有统计值——返回 IntSummaryStatistics,一个对象里有 getCount/getSum/getMin/getMax/getAverage。为什么用它:分别求 count/sum/min/max/average 要遍历 5 次(浪费),summaryStatistics 一次遍历全算出(高效)。配合 groupingBy + summarizingInt分组统计(每组的所有统计值)。理解「summaryStatistics 一次遍历拿所有统计值(count/sum/min/max/average)、比分别求(遍历5次)高效、配合 groupingBy+summarizingInt 分组统计」,就掌握了 summaryStatistics。

六、互转与实践

数值流和对象流的互转,以及实践建议:

对象流 → 数值流:
  mapToInt/mapToLong/mapToDouble(取值函数)
  list.stream().mapToInt(Order::getAmount)  // Stream<Order> → IntStream
  → 提取数字字段、转成数值流做数值运算

数值流 → 对象流:
  boxed():int → Integer(装箱回对象流)
    IntStream.range(0,5).boxed()  → Stream<Integer>
    → 需要收集成 List<Integer> 时用(toList 需要对象流)
  mapToObj(fn):int → 任意对象
    IntStream.range(0,5).mapToObj(i -> "item" + i)  → Stream<String>

实践建议:
  ① 对数字求和/平均/统计 → mapToInt 转数值流,用 sum/average/summaryStatistics
     list.stream().mapToInt(Order::getAmount).sum()
     (而非 map(...).reduce(0, Integer::sum) 一路装箱)
  ② 数字范围遍历 → IntStream.range/rangeClosed(替代 for)
  ③ 需要收集成 List<Integer> → 数值流 boxed() 再 collect
  ④ 多个统计值 → summaryStatistics 一次算

一句话:处理数字用数值流(避免装箱、有专门方法),
  对象流→数值流用 mapToInt,数值流→对象流用 boxed/mapToObj

数值流和对象流互转对象流→数值流用 mapToInt(提取数字字段做数值运算);数值流→对象流用 boxed(int→Integer,需要 collectList<Integer> 时)或 mapToObj(int→任意对象)。实践:求和/平均/统计用 mapToInt+sum/average/summaryStatistics(而非 map+reduce 一路装箱)、数字范围用 range/rangeClosed、收集成 List 用 boxed 再 collect、多个统计用 summaryStatistics。理解「互转:对象流→数值流用 mapToInt、数值流→对象流用 boxed(装箱)/mapToObj;实践:求和用 mapToInt.sum(别 map.reduce 装箱)、范围用 range、收集用 boxed、多统计用 summaryStatistics」,就掌握了互转和实践。

记忆钩子:「IntStream/LongStream/DoubleStream=基本类型特化流,核心价值避免 Stream的装箱拆箱开销(Integer 是堆上对象、运算要拆箱、大量数据时开销+内存+GC 压力),数值流直接用 int(无装箱、省内存);专属方法:sum()/average()/max()/min()(普通 Stream 没有、比 reduce 简洁);range(a,b)生成[a,b)/rangeClosed[a,b]替代 for 循环;summaryStatistics()一次遍历拿 count/sum/min/max/average(比分别求高效);互转:对象流→数值流 mapToInt、数值流→对象流 boxed(装箱)/mapToObj;处理数字用 mapToInt.sum 别用 map.reduce(一路装箱)」

七、常见误区与追问

  • 误区:Stream 和 IntStream 性能一样。 不一样——Stream 的元素是 Integer 对象,数值运算要装箱/拆箱、创建对象、有 GC 压力;IntStream 直接用 int(基本类型),全程无装箱拆箱、不创建对象、更快更省内存;大量数字处理差别明显。
  • 误区:求一堆数字的和用 stream().map(…).reduce(0, Integer::sum)。 这样一路装箱、低效;应该用 list.stream().mapToInt(…).sum()——mapToInt 转成 IntStream(直接用 int),sum() 直接求和、无装箱;简洁且高效。
  • 误区:range(0, 5) 包含 5。 range(a, b) 是左闭右开 [a, b)、不含 b——range(0, 5) 是 0,1,2,3,4;要包含 b 用 rangeClosed(a, b)(闭区间 [a, b])——rangeClosed(1, 5) 是 1,2,3,4,5。
  • 误区:数值流能直接 collect(Collectors.toList())。 不能直接——Collectors.toList() 需要对象流;数值流要先 boxed() 装箱成 Stream 再 collect(toList()),或用数值流自己的 toArray() 拿 int[];数值流的 collect 签名和对象流不同。
  • 追问:为什么处理数字要用 IntStream 而不是 Stream 避免装箱拆箱开销——Stream 的每个元素是 Integer 对象(堆上),做数值运算要拆箱成 int 计算、有时装箱回 Integer,大量数据时这些装箱/拆箱 + 对象创建 + GC 开销累积很可观;IntStream 直接用 int(栈上基本类型),全程无装箱、不创建对象,更快更省内存。
  • 追问:summaryStatistics 有什么用? 一次遍历拿到所有统计值——返回 IntSummaryStatistics,一个对象里就有 getCount/getSum/getMin/getMax/getAverage;如果分别求这些统计值要遍历流 5 次,summaryStatistics 一次遍历全算出来、高效;配合 groupingBy + summarizingInt 还能做分组统计(每组的所有统计值)。
  • 追问:对象流和数值流怎么互转? 对象流→数值流:mapToInt/mapToLong/mapToDouble(传取值函数,如 list.stream().mapToInt(Order::getAmount));数值流→对象流:boxed()(装箱回 Stream,需要 collect 成 List 时用)或 mapToObj(fn)(映射成任意对象,如 IntStream.range(0,5).mapToObj(i -> “item”+i))。

八、加强记忆

IntStreamLongStreamDoubleStream 是「基本类型特化流」——处理 int/long/double 时直接用基本类型,避免普通 Stream<Integer> 的装箱/拆箱开销核心价值是避免装箱拆箱Stream<Integer> 的元素是 Integer 对象(堆上),数值运算要拆箱成 int 算、有时装箱回 Integer,大量数据时装箱拆箱 + 对象创建 + GC 开销可观;数值流直接用 int(栈上基本类型),全程无装箱、不创建对象、更快更省内存。专属数值方法(普通 Stream 没有):sum()average()(OptionalDouble)、max()/min()(OptionalInt)——比 reduce(0, Integer::sum) 简洁高效。range(a,b) 生成 [a,b)rangeClosed(a,b) 生成 [a,b]——替代传统 for 循环。summaryStatistics() 一次遍历拿 count/sum/min/max/averageIntSummaryStatistics,比分别求遍历 5 次高效,配 groupingBy+summarizingInt 做分组统计)。互转:对象流→数值流用 mapToInt(提取数字字段),数值流→对象流用 boxed(装箱,需 collect 成 List 时)或 mapToObj(映射成对象)。实践:处理数字用 mapToInt(...).sum()(别用 map(...).reduce(...) 一路装箱)。一句话「数值流 IntStream/LongStream/DoubleStream 直接用基本类型避免 Stream的装箱拆箱(省内存省 GC);专属 sum/average/max/min(比 reduce 简洁)、range/rangeClosed 替代 for、summaryStatistics 一次拿全部统计;对象流→数值流 mapToInt、数值流→对象流 boxed/mapToObj;求和用 mapToInt.sum 别 map.reduce」。