流是一系列与特定存储机制无关的元素——实际上,流并无“存储”之说。php
使用流,无需迭代集合中的元素,就能够提取/操做特定的元素java
假设我要生成一个随机序列,范围在5到100之间,不重复,随机生成7个数字,并且要排序,最后输出序列,能够这么作:python
public static void main(String[] args) { new Random(47) // 种子 .ints(5, 100) .distinct() .limit(7) .sorted() .forEach(System.out::println); }
整个过程是一个工做流,它不须要单独提取出这个序列在进行操做。c++
若是要实现刚刚的功能,而不使用流,就可能会是这样子:正则表达式
public static void main(String[] args) { Random random = new Random(47); SortedSet<Integer> set = new TreeSet<>(); while (set.size() < 7){ int r = random.nextInt(100); if(r >= 5){ set.add(r); } } System.out.println(set); }
流操做的类型有三种:数据库
.forEach()
.sum()
能够用Steam.of
将对象数组/一组元素转换为 流编程
String[] arr = {"C++ ", "Python ", "Java"}; Stream.of(arr).forEach(System.out::print); Stream.of("C++ ", "Python ", "Java ").forEach(System.out::print);
每一个集合也能够用.stream()
来产生一个流数组
List<String> strList = Arrays.asList("C++ ", "Python ", "Java "); strList.stream().forEach(System.out::print); // C++ Python Java
对于原始数据类型的数组/列表,能够用Arrays.stream()
安全
Steam.of()
,输出的是一个地址int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Arrays.stream(arr).forEach(System.out::println); // 输出元素1 2 3 4 ... Stream.of(arr).forEach(System.out::println); // 输出地址 [I@214c265e
生成4个随机整数,输出服务器
ints(n)
:控制流的大小为4,生成4个整数,返回一个IntStream
forEach(System.out::println)
:打印每一个元素Random random = new Random(47); // 47是参数,种子 random.ints(4).forEach(System.out::println);
生成5个随机整数,只取前3个整数
Random random = new Random(); random.ints(5).limit(3).forEach(System.out::println);
建立6个随机整数,范围在10~20之间
ints(m,n)
:生成[m,n)
之间的整数,返回一个IntStream
ints(m,n)
的方法,要加limit()
,否则就是无限循环Random random = new Random(); random.ints(10,20).limit(6).forEach(System.out::println);
也能够用init(streamSize,numberOrigin,numberBound)
来建立:
Random random = new Random(); random.ints(6,10,20).forEach(System.out::println);
假设我要建立一个[1,100]
的连续序列,求它的和,能够用 IntStream
提供的 range(m,n)
方法
public static void main(String[] args) { // 1-100的序列数组 int[] intsArr = IntStream.range(1, 101).toArray(); int sum = Arrays.stream(intsArr).sum(); System.out.println(sum); }
能够写的再简单一点:
public static void main(String[] args) { // 1-100的序列数组 int sum = IntStream.range(1, 101).sum(); System.out.println(sum); }
对比传统的方法,就会以为用 流 会方便不少:
// 传统方法 public static void main(String[] args) { int sum = 0; for(int i = 1; i <= 100;++i){ sum += i; } System.out.println(sum); }
Stream.generate()
建立Stream.generate()
返回无限流,咱们能够自定义生成的元素
生成5个随机整数
Stream.generate(() -> { return new Random().nextInt(); }).limit(5).forEach(System.out::println);
也能够简写成:
Stream.generate(new Random()::nextInt).limit(5).forEach(System.out::println);
Stream.iterate()
建立方法原型:
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) { // ... }
第一个参数:种子
第二个参数:方法(lambda表达式)
将 种子 传递给 方法,方法的运行结果 做为流的下一个元素,添加到流中,存储起来,做为下次调用iterate()
的第二个参数
用iterate()
生成斐波那契数列
iterate()
只记忆结果
public class Generator { private int x = 0; Stream<Integer> numbers(){ return Stream.iterate(1,(i)->{ int res = x + i; x = i; return res; }); } public static void main(String[] args) { // 生成前10项 new Generator().numbers().limit(10).forEach(System.out::println); // 生成第10到20项 new Generator() .numbers() .skip(10) // 过滤前10个 .limit(10) // 而后取10个 .forEach(System.out::println); } }
Arrays.stream()
和 Stream.of()
Arrays
类中含有一个名为 stream()
的静态方法用于把 int/long/double
数组 转换成为流。
int、double、long等基本类型的数组,用 Arrays.stream(数组名)
进行流的操做
若是是String(String是一个对象),能够用 Stream.of(对象数组名)
进行流的操做
Stream.of()
和 Arrays.stream()
是有区别的:
Integer
、Long
),两个方法是同样的结果Integer[] intArr = {1, 2, 3, 4}; Arrays.stream(intArr).forEach(System.out::print); // 1234 Stream.of(intArr).forEach(System.out::print); // 1234
int
、long
),两个方法返回的结果不同int[] intArr = {1, 2, 3, 4}; Arrays.stream(intArr).forEach(System.out::print); // 1234 Stream.of(intArr).forEach(System.out::print); // [I@214c265e
这是由于int[]
传入Stream.of()
会被看成一个对象,而传入Arrays.stream()
会被当成一个数组
什么是流的中间操做?就是修改流中的元素,返回一个修改后的Stream。
一个流建立以后有6个元素,我跳过前3个,只取最后3个,那中间操做就是 “跳过前三个”
peek()
跟踪和调试peek()
的目的是:无修改的查看流中的元素
peek()
参数可接收lambda表达式
当流中的一个元素经过管道的时候,就会调用一次peek()
例1
public static void main(String[] args) { String[] str = {"java ", "c++ ", "php ", "dart "}; Stream.of(str) .skip(1) .peek(System.out::print) // 输出当前流的状况 .map(s -> "第1次变化:" + s) // 映射,修改str中字符串的值 .peek(System.out::print) .forEach(System.out::println); }
例1-输出结果:
c++ 第1次变化:c++ 第1次变化:c++ php 第1次变化:php 第1次变化:php dart 第1次变化:dart 第1次变化:dart
例2
public static void main(String[] args) { String[] str = {"java ", "c++ ", "php ", "dart "}; Stream.of(str) .skip(1) .peek(System.out::print) .map(String::toUpperCase) // 映射,将流中的元素全转成大写 .forEach(System.out::print); }
例2-输出结果
c++ C++ php PHP dart DART
sorted()
排序sorted()
接收一个lambda表达式,也能够接收一个 比较器(sorted()
预设了一些默认的比较器)
基本类型数组排序
我查了资料,发现 基本类型的数组的sorted()
不能传比较器,它们的包装类就能够
public static void main(String[] args) { // 基本类型数组 int[] arr = {1, 18, 12, 16, 4}; // 默认正序 Arrays.stream(arr).sorted().forEach(System.out::println); // 1 4 12 16 18 }
对象/包装类数组排序
public static void main(String[] args) { // Integer类型数组 Integer[] integerArr = {1, 18, 12, 16, 4}; // 正序 Arrays.stream(integerArr).sorted().forEach(System.out::println); // 1 4 12 16 18 // 逆序 Arrays.stream(arr).sorted(Comparator.reverseOrder()).forEach(System.out::println); // 18 16 12 4 1 }
集合排序
public static void main(String[] args) { // List 集合 List<Integer> list = new LinkedList<>(Arrays.asList(1,18,12,16,4)); // 正序 list.stream().sorted().forEach(System.out::println); // 1 4 12 16 18 // 逆序 list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println); // 18 16 12 4 1 }
distinct()
去重消除流中重复的元素
public static void main(String[] args) { // 数组 Integer[] arr = {1, 1, 1, 2, 2}; Arrays.stream(arr).distinct().forEach(System.out::println); // 1 2 }
filter(Predicate)
过滤器filter()
接收到参数是一个函数,若是函数返回 true
,则保留这些元素。返回false
则删除这些元素
假定有一个数组,保留其中的奇数:
public class Main { public static void main(String[] args) { // 数组 Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; Arrays.stream(arr).filter(Main::Odd).forEach(System.out::println); // 1 3 5 7 } // 奇数返回true public static Boolean Odd(int number) { return number % 2 == 1; } }
一样的,filter()
也接收正则表达式
public static void main(String[] args) { // 数组 Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; Arrays.stream(arr).filter(num -> num % 2 == 1).forEach(System.out::println); }
map()
相关 - 映射到元素map(Function)
:将函数操做应用在输入流的元素中,并将返回值传递到输出流中。mapToInt(ToIntFunction)
:操做同上,但结果是 IntStream。mapToLong(ToLongFunction)
:操做同上,但结果是 LongStream。mapToDouble(ToDoubleFunction)
:操做同上,但结果是 DoubleStream。public static void main(String[] args) { // 数组 Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; Arrays.stream(arr) .map(s -> s * 3) // 每一个元素 × 3 .forEach(System.out::println); // 2 4 9 16 10 12 14 }
skip(long n)
跳过前n个元素skip(long n)
能够省略流的前n个元素
public static void main(String[] args) { int[] arr= {1,2,3,4,5}; // 省略前 3 个 Arrays.stream(arr).skip(3).forEach(System.out::println); // 4 5 }
limit(long n)
保留前n个元素limit(long n)
能够保留前n个元素,其余的不要
public static void main(String[] args) { int[] arr= {1,2,3,4,5}; // 保留前 3 个 Arrays.stream(arr).limit(3).forEach(System.out::println); // 1 2 3 }
parallel()
流的并行处理parallel()
可实现多处理器并行操做。实现原理为将流分割为多个(一般数目为 CPU 核心数)并在不一样处理器上分别执行操做。
使用场景(前提是确保线程的安全):
假设这样一个场景:有一个商品的分类ld,要根据分类Id,去数据库找具体的信息(IO操做)
传统的遍历
public static void main(String[] args) { List<Category> list = getCategoryIdList(); // 获取商品分类的Id // 普通的遍历 long start = System.currentTimeMillis(); for (Category category : list) { System.out.println(getById(category.id)); } long end = System.currentTimeMillis(); System.out.println("普通遍历 运行时间:" + (end - start) + "毫秒"); }
使用 parallel()
遍历
遍历的结果是无序的
运行时间:607毫秒
public static void main(String[] args) { List<Category> list = getCategoryIdList(); // 获取商品分类的Id // 并行遍历 long start = System.currentTimeMillis(); list.stream().parallel().forEach(category -> { // IO 操做 System.out.println(getById(category.id)); // 根据分类Id,查询具体信息 }); long end = System.currentTimeMillis(); System.out.println("parallel() 运行时间:" + (end - start) + "毫秒"); }
使用 Java8 的 parallel 能够加快某些操做的速度,但若是是一些简单的操做,那就得不偿失了
此外,parallel()
遍历的结果是无序的
终端操做:获取流的最终结果,没法再日后传递流,好比打印、获取集合、查找,都算是终端操做
Optional
对象Optional
类 隐藏了可能存在空指针的不肯定性
一些终端操做,会返回一个Optional
对象,由于这些操做,不能保证预期结果必定存在,也就是隐藏了空指针的信息。若是流是空的,那就会返回Option.empty
findFirst()
返回一个包含第一个元素的 Optional 对象,若是流为空则返回 Optional.empty
findAny()
返回包含任意元素的 Optional 对象,若是流为空则返回 Optional.empty
max()
和 min()
返回一个包含最大值或者最小值的 Optional 对象,若是流为空则返回 Optional.empty
reduce()
再也不以 identity
形式开头,而是将其返回值包装在 Optional 中。
(identity
对象成为其余形式的 reduce()
的默认结果,所以不存在空结果的风险)
findFirst()
public static void main(String[] args) { String [] str1 = {"java","python","c++"}; String [] str2 = {}; System.out.println(Stream.of(str1).findFirst()); // Optional[java] System.out.println(Stream.of(str2).findFirst()); // Optional.empty }
findAny()
public static void main(String[] args) { String [] str1 = {"java","python","c++"}; String [] str2 = {}; System.out.println(Stream.of(str1).findAny()); // Optional[java] System.out.println(Stream.of(str2).findAny()); // Optional.empty }
max()
和 min()
public static void main(String[] args) { Integer[] arr1 = {1, 2, 3, 4, 5}; Integer[] arr2 = {}; System.out.println(Stream.of(arr1).max(Integer::compareTo)); // Optional[5] System.out.println(Stream.of(arr1).min(Integer::compareTo)); // Optional[5] System.out.println(Stream.of(arr2).min(Integer::compareTo)); // Optional.empty System.out.println(Stream.of(arr2).max(Integer::compareTo)); // Optional.empty }
解包 Optional:
isPresent()
:判断 Optional 对象中是否包含元素ifPresent(Consumer)
:若是 Optional对象中包含元素,就调用 Consumer方法(lambda表达式)get()
:获取 Optional对象的 元素orElse(otherObject)
:若是值存在则直接返回,不然生成 otherObject。orElseGet(Supplier)
:若是值存在则直接返回,不然使用 Supplier 函数生成一个可替代对象。orElseThrow(Supplier)
:若是值存在直接返回,不然使用 Supplier 函数生成一个异常。toArray()
返回数组toArray()
:将流按照数组返回
toArray(T[] a)
:生成自定义类型的数组
生成随机整型数组:
public static void main(String[] args) { int[] arr = new Random().ints(5).toArray(); // 生成生成5个int型,转换为int数组 }
用toArray(T[] a)
将集合转成数组
public static void main(String[] args) { List<String> list = new LinkedList<>(); list.add("java "); list.add("python"); String[] str = (String[]) list.toArray(new String[0]); Stream.of(str).forEach(System.out::print); // java python }
forEach()
遍历forEach(Consumer)
常见如 System.out::println
做为 Consumer 函数。
forEachOrdered(Consumer)
: 保证 forEach
按照原始流顺序操做。(parallel()
操做以后是无序的,用这个能够强制保持原始流的顺序)
两个函数都接收lambda表达式
使用forEach
遍历数组:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; // 接收lambda表达式 // i 就是数组中的元素 Arrays.stream(arr).forEach(i -> { // do something... });
传统方法遍历数组:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; for(Integer i : arr){ // do something... }
collect()
返回集合collect(Collector)
:使用 Collector 收集流元素到结果集合中。
collect(Supplier, BiConsumer, BiConsumer)
:同上,第一个参数 Supplier 建立了一个新的结果集合,第二个参数 BiConsumer 将下一个元素收集到结果集合中,第三个参数 BiConsumer 用于将两个结果集合合并起来。
生成随机数,保存到 LinkedList中
public static void main(String[] args) { LinkedList<Integer> list = new Random(47) .ints(10) .collect(LinkedList::new, LinkedList::add, LinkedList::addAll); for (Integer integer : list) { System.out.print(integer+ " "); } }
reduce()
组合使用reduce()
组合全部流中的元素
reduce(BinaryOperator)
:使用 BinaryOperator 来组合全部流中的元素。由于流可能为空,其返回值为 Optional。
reduce(identity, BinaryOperator)
:功能同上,可是使用 identity 做为其组合的初始值。所以若是流为空,identity 就是结果。
reduce(identity, BiFunction, BinaryOperator)
:更复杂的使用形式
identity
:组合函数的标识值,累加器的初始值。
BiFunction
:累加器,一个函数,用于将额外的元素合并到结果中
BinaryOperator
:用于组合两个值的关联、不干扰、无状态函数,必须与累加器函数兼容
只有在并行流中才会执行
使用Stream.reduce()
合并流的元素,并产生单个值
传统方法使用for循环求和
public static void main(String[] args) { int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = 0; for(int i : numbers){ sum += i; } System.out.println(sum); // 55 }
使用reduce()
求和
public static void main(String[] args) { int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = Arrays.stream(numbers).reduce(0, (a, b) -> a + b); System.out.println(sum); }
累加器的初始值设置为0,(a,b)-> a+b
是累加器,结果保存到第一个参数中
count()
统计统计流中的个数,返回值是long
类型
String[] arr = {"123", "456", "789"}; long cnt = Stream.of(arr).count(); System.out.println(cnt); // 3
max()
和 min()
最大数值max()
和 min()
:数值流操做无需 Comparator。返回一个Optional
对象数组最大值、最小值
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; System.out.println(Arrays.stream(arr).max()); // OptionalInt[10] System.out.println(Arrays.stream(arr).max().getAsInt()); // 10 System.out.println(Arrays.stream(arr).min()); // OptionalInt[1] System.out.println(Arrays.stream(arr).min().getAsInt()); // 1 }
max(Comparator)
和 min(Comparator)
max(Comparator)
:根据所传入的 Comparator 所决定的“最大”元素。min(Comparator)
:根据所传入的 Comparator 所决定的“最小”元素。Optional
对象public static void main(String[] args) { String[] str = {"java","c++","python"}; System.out.println(Arrays.stream(str).max(String::compareTo)); // Optional[python] System.out.println(Arrays.stream(str).max(String::compareTo).get()); // python }
sum()
求和求流元素的和
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = Arrays.stream(arr).sum(); System.out.println(sum); // 55 }
average()
平均值求流元素的平均值,返回一个OptionalDouble
对象
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; OptionalDouble average = Arrays.stream(arr).average(); System.out.println(average); // OptionalDouble[5.5] System.out.println(average.getAsDouble()); // 5.5 }
allMatch(Predicate)
:若是流的每一个元素提供给 Predicate 都返回 true ,结果返回为 true。在第一个 false 时,则中止执行计算。anyMatch(Predicate)
:若是流的任意一个元素提供给 Predicate 返回 true ,结果返回为 true。在第一个 true 时中止执行计算。noneMatch(Predicate)
:若是流的每一个元素提供给 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时中止执行计算。说简单,就是传一个lambda表达式,而后返回布尔值
判断数组中是否全都为奇数
public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 判断全部元素是否都为奇数 boolean flag = Arrays.stream(arr).allMatch(i -> i % 2 == 1); System.out.println(flag); }
findFirst()
:返回第一个流元素的 Optional,若是流为空返回 Optional.empty。findAny(
:返回含有任意流元素的 Optional,若是流为空返回 Optional.empty。public static void main(String[] args) { String[] str = {"java","c++","python"}; System.out.println(Arrays.stream(str).findFirst()); // Optional[java] System.out.println(Arrays.stream(str).findFirst().get()); // Java }