news 2026/4/15 13:10:32

普通流(Stream<T>)和原始类型特化流(IntStream, LongStream, DoubleStream)的区别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
普通流(Stream<T>)和原始类型特化流(IntStream, LongStream, DoubleStream)的区别

一、例题:计算商品总价(单价 × 数量)

1-1、方法一:

public static void test06(){ List<Product> productList = Arrays.asList( new Product("Book", 20.0, 2), new Product("Pen", 2.5, 4) ); List<Double> sumPricePer = productList.stream() .map(product -> product.getPrice() * product.getStock()) .collect(Collectors.toList()); System.out.println(sumPricePer.toString()); double sum = 0.0; for (Double price : sumPricePer) { sum += price; } System.out.println(sum); }

1-2、方法二:

public static void test07(){ List<Product> productList = Arrays.asList( new Product("Book", 20.0, 2), new Product("Pen", 2.5, 4) ); double sum = productList.stream() .mapToDouble(product -> product.getPrice() * product.getStock()) .sum(); System.out.println(sum); }

二、为什么Stream<Integer>没有.sum()

因为:

  • Stream<T>泛型接口,设计目标是处理任意引用类型(如String,Product,Integer)。
  • 不知道T是否是数字,更不知道如何对它求和。
  • 所以Stream<T>只提供通用操作:collect(),forEach(),filter()等,不提供sum()

2-1、包装类本身能做运算吗?(脱离 Stream 的情况)

普通 Java 代码中,包装类可以参与算术运算,因为 Java 会自动拆箱:

Integer a = 10; Integer b = 20; int c = a + b; // ✅ 自动拆箱:a.intValue() + b.intValue()

所以:在普通代码中,包装类可以做运算(靠自动拆箱)

Java 需要包装类,是因为泛型、集合(如List)、反射、序列化等机制只支持引用类型,不支持基本类型。


2-2、必须用包装类的场景

场景说明
泛型类/方法Optional<Integer>,Comparator<Double>
反射(Reflection)Method.invoke()返回Object,只能是引用类型
JSON/XML 序列化Jackson、Gson 等库处理的是对象,不是int
数据库 ORM(如 JPA)实体类字段通常是Integer,因为可能为NULL
表示“缺失值”Integer可以为null,而int必须有值(默认 0)

示例:为什么集合(Collection)必须用包装类?

// ❌ 编译错误!泛型不支持基本类型 List<int> numbers = new ArrayList<>(); // ✅ 正确:必须用包装类 List<Integer> numbers = new ArrayList<>();
原因:
  • Java 的泛型是通过类型擦除实现的底层只认Object
  • int不是Object的子类,不能被当作对象处理
  • IntegerObject的子类,可以放进ListMapSet等。

所以:没有包装类,你就无法把数字存进ArrayListHashMap等常用集合!


2-3、Java 的“双轨制”类型系统

Java 有两类数据类型:

类型示例特点
基本类型(Primitive)int,double,boolean存储在上,高效,无方法
引用类型(Reference)Integer,Double,String存储在上,是对象,可为null

为什么不能统一成一种?

因为:

  • 性能:基本类型快、省内存。
  • 面向对象:Java 是 OOP 语言,希望“一切皆对象”。

于是 Java 采取了折中方案:保留基本类型保性能,引入包装类来“对象化”基本值


自动装箱/拆箱:让两者无缝协作

为了不让开发者痛苦地手动转换,Java 5 引入了:

  • 自动装箱(Autoboxing)Integer i = 100;Integer.valueOf(100)
  • 自动拆箱(Unboxing)int x = i;i.intValue()

那为什么不全用包装类?

因为:

  • 性能开销:每个Integer都是对象,占用堆内存,触发 GC。
  • 空指针风险Integer a = null; int b = a;NullPointerException
  • 比较陷阱
Integer a = 128, b = 128; System.out.println(a == b); // false!因为超出缓存范围(-128~127)

所以:能用基本类型就用基本类型,只有需要“对象特性”时才用包装类

三、为什么要有原始类型特化流IntStream/DoubleStream

Java 的泛型不能直接使用基本类型(如int,double只能用包装类Integer,Double)。

如果对大量数字做运算,频繁装箱/拆箱会带来性能开销内存浪费

为了解决这个问题,Java 8 引入了三个原始类型特化的 Stream

原始类型对应的 Stream 类型
intIntStream
longLongStream
doubleDoubleStream

它们不涉及装箱/拆箱,性能更高,且提供专门的数值操作方法(如sum(),average(),max()等)


四、中间操作的返回类型分类

4-1. 返回Stream<T>的中间操作(最常见)

这些操作保持流的“对象”性质,适用于任意类型:

Stream<String> s1 = list.stream() .filter(x -> x.length() > 3) // Stream<T> .map(String::toUpperCase) // Stream<T> .peek(System.out::println) // Stream<T> .distinct() // Stream<T> .sorted() // Stream<T> .limit(5) // Stream<T> .skip(2); // Stream<T>

所有这些方法都返回Stream<T>,所以可以链式调用。


4-2. 返回原始类型流的中间操作(特殊转换)

当你需要对象流转为数值流时,使用以下方法:

方法返回类型说明
.mapToInt(ToIntFunction)IntStream把每个元素映射成int
.mapToLong(ToLongFunction)LongStream映射成long
.mapToDouble(ToDoubleFunction)DoubleStream映射成double
示例:
List<Product> products = ...; // 转成 DoubleStream(用于价格计算) DoubleStream ds = products.stream() .mapToDouble(p -> p.getPrice()); // 返回 DoubleStream // 转成 IntStream(用于库存) IntStream is = products.stream() .mapToInt(p -> p.getStock()); // 返回 IntStream

⚠️ 一旦变成DoubleStream,你就不能再调用.map()(因为它是DoubleStream的方法,不是Stream的),而要用.mapToDouble().map()(但参数是DoubleUnaryOperator)。


4-3. 原始类型流自己的中间操作

DoubleStream也有自己的中间操作,比如:

DoubleStream ds = products.stream() .mapToDouble(p -> p.getPrice()) .filter(d -> d > 10.0) // DoubleStream .map(d -> d * 1.1) // DoubleStream(注意:这里是 DoubleUnaryOperator) .sorted(); // DoubleStream

但注意:这里的.map()接收的是DoubleUnaryOperator(函数式接口:double → double),不是Function<Double, Double>


五、终端操作的区别

流类型支持的终端操作(部分)
Stream<T>collect(),forEach(),findFirst(),anyMatch()...
IntStreamsum(),average(),max(),min(),summaryStatistics()...
LongStream同上
DoubleStream同上

重点:只有IntStream/LongStream/DoubleStream才有.sum()方法!


六、类型转换图(简化版)

Stream<Product> │ ├─ .map(...) ────────────────→ Stream<R> │ ├─ .mapToInt(...) ───────────→ IntStream ├─ .mapToLong(...) ──────────→ LongStream └─ .mapToDouble(...) ────────→ DoubleStream │ ├─ .sum() → int / long / double └─ .boxed() → Stream<Integer> / Stream<Long> / Stream<Double>

💡如果你想把DoubleStream转回Stream<Double>,可以用.boxed()

List<Double> prices = products.stream() .mapToDouble(Product::getPrice) .boxed() .collect(Collectors.toList());
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/28 21:57:20

AIGC疑似度30%和60%有什么区别?如何解读检测报告的数值

AIGC疑似度30%和60%有什么区别如何解读检测报告的数值的核心问题是什么&#xff1f;关于AIGC疑似度30%和60%有什么区别这个问题&#xff0c;我们需要从基础概念开始理解。AIGC检测技术是近年来随着AI写作工具普及而快速发展的领域&#xff0c;它的出现改变了学术界和内容创作领…

作者头像 李华
网站建设 2026/4/4 5:01:06

未来AIGC检测技术会如何发展?AI检测的趋势预测

未来AIGC检测技术会如何发展AI检测的趋势预测的核心问题是什么&#xff1f; 关于未来AIGC检测技术会如何发展这个问题&#xff0c;我们需要从基础概念开始理解。AIGC检测技术是近年来随着AI写作工具普及而快速发展的领域&#xff0c;它的出现改变了学术界和内容创作领域对原创性…

作者头像 李华
网站建设 2026/4/13 18:34:12

14:lingbot-vla-4b VLA基础模型深度解析

作者&#xff1a; HOS(安全风信子) 日期&#xff1a; 2024-10-04 主要来源平台&#xff1a; ModelScope 摘要&#xff1a; 本文深度解析蚂蚁灵波科技开源的lingbot-vla-4b实用型VLA基础模型&#xff0c;基于9种双臂机器人20,000小时真实世界数据预训练&#xff0c;在仿真与真机…

作者头像 李华
网站建设 2026/4/10 23:20:45

西门子 S7 - 1200 PLC 控制 5 轴伺服项目全解析

西门子s7-1200plc控制5轴伺服&#xff0c;采用结构化编程&#xff0c;触摸屏采用威纶通&#xff0c;项目实现以下功能&#xff0c; 1.plc程序结构 采用结构化编程&#xff0c;每一功能为模块化设计&#xff0c;功能:自动-手动-单步-暂停-伺服断电保持-报警功能等等。 每个功能块…

作者头像 李华