目录
- 一、流的初始化与转换:
- 二、Stream的特性
- 三、流的常见操作
- 四、流操作与传统循环操作的对比
Java8中提供了Stream对集合操作作出了极大的简化,学习了Stream之后,我们以后不用使用for循环就能对集合作出很好的操作。
一、流的初始化与转换:
Java中的Stream的所有操作都是针对流的,所以,使用Stream必须要得到Stream对象:
1、初始化一个流:
Stream stream = Stream.of(“a”, “b”, “c”);
2、数组转换为一个流:
String [] strArray = new String[] {“a”, “b”, “c”};
stream = Stream.of(strArray);
或者
stream = Arrays.stream(strArray);
3、集合对象转换为一个流(Collections):
List list = Arrays.asList(strArray);
stream = list.stream();
4、集合对象转换为一个并行流(Collections):
List list = Arrays.asList(strArray);
stream = list.parallelstream();
二、Stream的特性
stream()优点:无存储。对大数据量的集合的循环处理,stream拥有极大的优势,完全可以用stream去代替for循环。
stream()介绍:是java对集合操作的优化,相较于迭代器,使用Stream的速度非常快,并且它支持并行方式处理集合中的数据,默认情况能充分利用cpu的资源。同时支持函数式编程,代码非常简洁。
1 Stream是一种用来计算数据的流,它本身并没有存储数据。你可以认为它是对数据源的一个映射或者视图。
2 Stream的工作流程是:获取数据源->进行一次或多次逻辑转换操作->进行归约操作形成新的流(最后可以将流转换成集合)。
3 stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
4 为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
5 惰式执行:stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
6 可消费性:stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
中间操作惰性执行:一个流后面可以跟随0到多个中间操作,主要目的是打开流,并没有真正的去计算,而是做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历,并没有消耗资源。
还有多个中间操作的话,这里的时间复杂度并不是n个for循环,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在Terminal操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
流的末端操作只能有一次: 当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。之后如果想要操作就必须新打开流。
关于流被关闭不能再操作的异常:stream has already been operated upon or closed
意思是流已经被关闭了,这是因为当我们使用末端操作之后,流就被关闭了,无法再次被调用,如果我们想重复调用,只能重新打开一个新的流。
三、流的常见操作
package com.cainiao.Stream.流的基操;
import org.junit.Test;
import java.text.Collator;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test1 {
// 公共方法 字符串转换为字符流
public static Stream<Character> caseToCharacterStream(String s){
List<Character> list = new ArrayList<>();
for (char c : s.toCharArray()) {
list.add(c);
}
return list.stream();
}
@Test
public void filter(){
List<String> list = Arrays.asList("abc","bce","刺客信条","张无忌","张无忌");
// 1 filter 筛选符合条件的元素
List<String> filter = list.stream().filter(s -> s.contains("a")).collect(Collectors.toList());
System.out.println(filter); //[abc]
System.out.println("................................................................");
// 2 distinct 剔除重复元素(不适用对象元素,因为对象存储的是地址,不是固定数值)
List<String> distinct = list.stream().distinct().collect(Collectors.toList());
System.out.println(distinct); //[abc, bce, 刺客信条, 张无忌]
System.out.println("................................................................");
// 3 limit 获取流中前几个元素
List<String> limit = list.stream().limit(2).collect(Collectors.toList());
System.out.println(limit); //[abc, bce]
System.out.println("................................................................");
// 4 skip 获取流中除去前几个元素的其他所有元素(与limit相反)
List<String> skip = list.stream().skip(2).collect(Collectors.toList());
System.out.println(skip);//[刺客信条, 张无忌, 张无忌]
System.out.println("................................................................");
// 5 map 对流中所有元素做统一处理
List<String> map = list.stream().map(s -> s + "yyds").collect(Collectors.toList());
System.out.println(map); //[abcyyds, bceyyds, 刺客信条yyds, 张无忌yyds, 张无忌yyds]
System.out.println("................................................................");
// 6 flatmap的核心作用:把几个小的集合分别转换成流,再把各个流转换为一个流,流转集合,以此达到小集合合并到大集合的作用。
// Stream<Character> characterStream = list.stream().flatMap(s -> caseToCharacterStream(s));
Stream<Character> characterStream = list.stream().flatMap(Test1::caseToCharacterStream); //lamadar简化写法 返回字符流
characterStream.forEach(System.out::println); //简写 characterStream.forEach(s -> System.out.println(s));
// 输出a
//b
//c
//b
//c
//e
//刺
//客
//信
//条
//张
//无
//忌
//张
//无
//忌
// 过程解析 :第(1)步 [a,b,c,y,y,d,s] [b,c,e,y,y,d,s] ... 第(2)步 【a,b,c,y,y,d,s,b,c,e,...,y,y,d,s】扁平化操作,把所有流合并为一个流再返回
Stream<Stream<Character>> streamStream = list.stream().map(Test1::caseToCharacterStream);//返回流中流对象(相当于流的集合)
// streamStream.forEach(System.out::println); 不能与下面的方法同时打开执行,因为此方法执行完会关闭流,然后下面那个遍历流中流的方法就会访问不到而报错
/*java.util.stream.ReferencePipeline$Head@71c7db30
java.util.stream.ReferencePipeline$Head@19bb089b
java.util.stream.ReferencePipeline$Head@4563e9ab
java.util.stream.ReferencePipeline$Head@11531931
java.util.stream.ReferencePipeline$Head@5e025e70
*/
// 过程解析:只有(1)步 [a,b,c,y,y,d,s] [b,c,e,y,y,d,s] ...就返回
streamStream.forEach(s ->s.forEach(System.out::println)); //简写 streamStream.forEach(s ->s.forEach(c -> System.out.println(c)));
// 输出a
//b
//c
//b
//c
//e
//刺
//客
//信
//条
//张
//无
//忌
//张
//无
//忌
System.out.println("................................................................");
// 7 sorted 返回排序后的流
List<String> sort = list.stream().sorted().collect(Collectors.toList());
System.out.println(sort); //[abc, bce, 刺客信条, 张无忌, 张无忌] 不能排序汉语
// 汉语排序
List<String> sortChinese = list.stream().sorted(Collator.getInstance(Locale.CHINA)).collect(Collectors.toList());
System.out.println(sortChinese); //[abc, bce, 刺客信条, 李光, 马超, 张无忌, 张无忌]
// 汉语反序
List<String> sortReverseChinese = list.stream().sorted(Collections.reverseOrder(Collator.getInstance(Locale.CHINA))).collect(Collectors.toList());
System.out.println(sortChinese);
System.out.println("................................................................");
// 8 anyMatch 集合中是否有满足匹配条件的元素 boolean
boolean 刺 = list.stream().anyMatch(s -> s.contains("刺"));
System.out.println(刺);//true
System.out.println("................................................................");
// 9 allMatch 集合中是否都满足匹配条件的元素 boolean
boolean result = list.stream().allMatch(s -> s.length()>1);
System.out.println(result);// true
System.out.println("................................................................");
// 9 noneMatch 集合中是否都不满足匹配条件的元素 boolean
boolean none = list.stream().noneMatch(s -> s.length()>1);
System.out.println(result);// true
System.out.println("................................................................");
// 10 findAny 返回集合中任意一个元素
Optional<String> any = list.stream().findAny();
System.out.println(any);//Optional[abc]
if (any.isPresent()) System.out.println(any.get()); //判断是否有值,有值就返回取到的值,因为单行流方便的原因,会只取第一个。并行流会随机取
// abc
Optional<String> any2=list.parallelStream().findAny();
System.out.println(any2);//Optional[刺客信条]
System.out.println("................................................................");
// 11 findFirst 返回集合中第一个元素
Optional<String> first = list.stream().findFirst();
first.ifPresent(System.out::println); // 简写 if(first.isPresent()) System.out.println(first.get());
System.out.println("................................................................");
// 12 foreach 对集合中元素进行遍历
System.out.println("................................................................");
// 13 collect 将流转换成其他像是 :list set map
Set<String> toSet = list.stream().collect(Collectors.toSet()); //set 去重 无序
System.out.println(toSet);//[abc, bce, 刺客信条, 张无忌]
Map<String, String> toMap = list.stream().collect(Collectors.toMap(v -> v, v -> v, (oldvalue, newvalue) -> newvalue));//第一个是key ,第二个是value,第三个是遇到重复后的处理
System.out.println(toMap);//{abc=abc, bce=bce, 刺客信条=刺客信条, 张无忌=张无忌}
System.out.println("................................................................");
// 14 reduce 将流中元素反复结合起来,得到一个值
//方式一 acc是每一步结合的结果,item是初始值(未指定,默认为空)
Optional<String> reduce1 = list.stream().reduce((acc, item) -> {
return acc + item;
});
System.out.println(reduce1); // Optional[abcbce刺客信条张无忌张无忌]
//方式二 acc是每一步结合的结果,item是初始值(未指定,默认为空)
String reduce2 = list.stream().reduce("666",(acc, item) -> {
return acc + item;
});
System.out.println(reduce2); // Optional[abcbce刺客信条张无忌张无忌]
// 方式三
// 15 获取集合中元素的数量
long count = list.stream().count();
System.out.println(count);//5
// 16 获取最大值,最小值,平均值
List<Integer> numList = Arrays.asList(1,2,5,10);
//方法一:
int max = numList.stream().mapToInt(x -> x).summaryStatistics().getMax();
int min = numList.stream().mapToInt(x -> x).summaryStatistics().getMin();
double average = numList.stream().mapToInt(x -> x).summaryStatistics().getAverage();
long sum = numList.stream().mapToInt(x -> x).summaryStatistics().getSum();
System.out.println("maxYear:"+max);
System.out.println("minYear:"+min);
System.out.println("average:"+average);
System.out.println("sum:"+sum);
//方法二:
Integer maxNum = numList.stream().max(Integer::compareTo).get();
Integer minNum = numList.stream().min(Integer::compareTo).get();
System.out.println("maxNum:"+maxNum);
System.out.println("minNum:"+minNum);
}
}
四、流操作与传统循环操作的对比
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
public class Java8Tester {
public static void main(String args[]){
System.out.println("使用 Java 7: ");
// 计算空字符串
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("列表: " +strings);
long count = getCountEmptyStringUsingJava7(strings);
System.out.println("空字符数量为: " + count);
count = getCountLength3UsingJava7(strings);
System.out.println("字符串长度为 3 的数量为: " + count);
// 删除空字符串
List<String> filtered = deleteEmptyStringsUsingJava7(strings);
System.out.println("筛选后的列表: " + filtered);
// 删除空字符串,并使用逗号把它们合并起来
String mergedString = getMergedStringUsingJava7(strings,", ");
System.out.println("合并字符串: " + mergedString);
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取列表元素平方数
List<Integer> squaresList = getSquares(numbers);
System.out.println("平方数列表: " + squaresList);
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
System.out.println("列表: " +integers);
System.out.println("列表中最大的数 : " + getMax(integers));
System.out.println("列表中最小的数 : " + getMin(integers));
System.out.println("所有数之和 : " + getSum(integers));
System.out.println("平均数 : " + getAverage(integers));
System.out.println("随机数: ");
// 输出10个随机数
Random random = new Random();
for(int i=0; i < 10; i++){
System.out.println(random.nextInt());
}
System.out.println("使用 Java 8: ");
System.out.println("列表: " +strings);
count = strings.stream().filter(string->string.isEmpty()).count();
System.out.println("空字符串数量为: " + count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("字符串长度为 3 的数量为: " + count);
filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选后的列表: " + filtered);
mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " + squaresList);
System.out.println("列表: " +integers);
IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
System.out.println("随机数: ");
random.ints().limit(10).sorted().forEach(System.out::println);
// 并行处理
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串的数量为: " + count);
}
private static int getCountEmptyStringUsingJava7(List<String> strings){
int count = 0;
for(String string: strings){
if(string.isEmpty()){
count++;
}
}
return count;
}
private static int getCountLength3UsingJava7(List<String> strings){
int count = 0;
for(String string: strings){
if(string.length() == 3){
count++;
}
}
return count;
}
private static List<String> deleteEmptyStringsUsingJava7(List<String> strings){
List<String> filteredList = new ArrayList<String>();
for(String string: strings){
if(!string.isEmpty()){
filteredList.add(string);
}
}
return filteredList;
}
private static String getMergedStringUsingJava7(List<String> strings, String separator){
StringBuilder stringBuilder = new StringBuilder();
for(String string: strings){
if(!string.isEmpty()){
stringBuilder.append(string);
stringBuilder.append(separator);
}
}
String mergedString = stringBuilder.toString();
return mergedString.substring(0, mergedString.length()-2);
}
private static List<Integer> getSquares(List<Integer> numbers){
List<Integer> squaresList = new ArrayList<Integer>();
for(Integer number: numbers){
Integer square = new Integer(number.intValue() * number.intValue());
if(!squaresList.contains(square)){
squaresList.add(square);
}
}
return squaresList;
}
private static int getMax(List<Integer> numbers){
int max = numbers.get(0);
for(int i=1;i < numbers.size();i++){
Integer number = numbers.get(i);
if(number.intValue() > max){
max = number.intValue();
}
}
return max;
}
private static int getMin(List<Integer> numbers){
int min = numbers.get(0);
for(int i=1;i < numbers.size();i++){
Integer number = numbers.get(i);
if(number.intValue() < min){
min = number.intValue();
}
}
return min;
}
private static int getSum(List numbers){
int sum = (int)(numbers.get(0));
for(int i=1;i < numbers.size();i++){
sum += (int)numbers.get(i);
}
return sum;
}
private static int getAverage(List<Integer> numbers){
return getSum(numbers) / numbers.size();
}
}
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
使用 Java 7:
列表: [abc, , bc, efg, abcd, , jkl]
空字符数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
平方数列表: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9
随机数:
-393170844
-963842252
447036679
-1043163142
-881079698
221586850
-1101570113
576190039
-1045184578
1647841045
使用 Java 8:
列表: [abc, , bc, efg, abcd, , jkl]
空字符串数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9.444444444444445
随机数:
-1743813696
-1301974944
-1299484995
-779981186
136544902
555792023
1243315896
1264920849
1472077135
1706423674
空字符串的数量为: 2