要点介绍:
原有的迭代器,禁止了高效的并发,所以不如Stream
可以从 集合、数组、生成器、迭代器中创建Stream流
可以用 limit、distinct、sorted改变Stream
可以用reduction操作符,从Stream中获得结果 例如(count()、max()、min()、findFirst()、findAny),可能会返回Option值
Optional的目的是为了安全的替代使用null值,可以借助ifPresent或者orElse方法
可以获取集合、数组、字符串和map里的Stream结果
Collections类的groupingBy和partitioningBy可以对Stream中内容分组,取得每个组的结果
每个原始类型都有专门的stream也有专门的函数式接扣
Stream和集合的区别
对集合进行迭代,很难进行并行运算
Stream看着和集合很类似
Stream不会改变源数据
Stream自己不会存储元素,元素存在于底层集合根据需要产生
Stream可能延迟执行,只在需要结果的时候执行。例如只要五个数,满足结果之后就停止执行了。
Stream 原则是,“做什么,而不是怎么去做”
使用stream操作的步骤
创建Stream
转换Stream
收集结果
1、都是依靠StreamSupport创建流
2、对于实现Collection接口的,可以使用新增的默认方法:Collection.stream()
3、对于数组或单个对象,可以使用Stream.of()来创建
4、Stream类的静态方法Stream.iteratoe()、和Stream.generaotr()可以创建无限的Stream
对于继承Collection接口的类,都可以使用其新增的方法,由spliterator创建
//创建顺序流
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
//创建一个并行流,允许返回顺序流
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
//1、创建单个元素的Stream
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
//2、根据不定长的参数数组创建Stream
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
//Arrays.Stream(values),该方法含有重载方法,可截取一部分来创建流
public static <T> Stream<T> stream(T[] array) {
return stream(array, 0, array.length);
}
//stream(array, 0, array.length);
public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) {
return StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false);
}
/**
* 1、Stream.generater生成无限长度的Stream,可以生成,有规律的数据,
* 前一个生成的值将作为第二个值的参数。序列的第一个数是seed 后续则为f(seed)
* public static Stream iterate(final T seed, final UnaryOperator f)
* UnaryOperator extends Function 只是限制了返回值和输入值必须为同一类型。
* 如下代码生成了从1开始递增1的无限递增序列
*/
Stream<Integer> infiniteStreamIterate=Stream.iterate(1,a->a+1);
/**
* 2、public static Stream generate(Supplier s)
* 可以生成任意类型的(随机/重复)无限数据序列
* 如下代码生成了无限个 1的Stream
*/
Stream<Integer> infiniteStreamGenerate=Stream.generate(()->1);
//filter筛选小于100的流
Stream<Integer> infiniteStreamIterateFilter=infiniteStreamIterate.filter(a->a<100);
//map将Integer转换为String
Stream<String> infiniteStreamGenerateMap=infiniteStreamGenerate.map(a->a.toString());
将一个会返回结果为Stream的map,的结果 水平展开后放到同一个Stream而不是单独的Stream
区别:
//map和flatMap的区别
//将String切割成,Character的Stream
Function<String, Stream<Character>> function = a -> {
List<Character> characterList = new ArrayList<>();
for (char c : a.toCharArray()) {
characterList.add(c);
}
return characterList.stream();
};
String[] stringArray = {"小刘", "小李", "小王", "小张"};
Stream<String> nameStream = Arrays.stream(stringArray);
//直接利用map收集结果 {"小刘", "小李", "小王", "小张"} ->[[小、刘],[小、李].....]
Stream<Stream<Character>> nameCharacterStream = nameStream.map(function);
//利用flatMap 水平收集Stream {"小刘", "小李", "小王", "小张"} ->[小、刘,小、李.....]
Stream<Character> charStream = nameStream.flatMap(function);
Stream.limit(n) 截取前n个元素,如果流中元素少与n则返回全部
Stream.skip(m)丢弃前m个元素
Stream.peek (Function) 产生一个与原来的元素一样的流,但是每次获取元素时都会调用一下一个函数,便于调试
**无状态:**流中的元素互不相关,单个元素的筛选结果与其他元素无关
**Stream.distinct() 去重 **Stream uniqueWord=Stream.of({"小刘“,“小刘”}).distinct();
**Stream.sort() 排序 **Stream.sorted(Comparator.comparing(String::length).reversed());
findFirst 取出流中第一个值,一般与filter一起用 取出流的第一个值
findAny 也是取出流中第一个值 但是在并行流中并不一定是流序列的第一个 而是时间上第一个满足条件的值,这个值满足条件且第一个被取出但不一定是序列的第一个
anyMatch 接收predict参数,判断流中是否有符合条件的元素,对应的还有allMatch和noneMatch 用于判断所有元素都符合,和所有元素都不符合的情况
说明:用上一个操作的结果,作为后一个操作的参数,一直到整个流执行完。有点像lambda函数中的函数复合
除了情况1 返回对应的 可选值Optional类型,其他两种情况都直接返回 对应类型的对象。
情况1:
Optional reduce~~(~~BinaryOperator accumulator);
//调用按理
nameStream.reduce((s1,s2)->s1+s2);
//源码说明:
//使用关联累积函数对此流的元素执行聚合,并返回描述缩减值的Optional (如果有)。
//这相当于:
// boolean foundAny = false;
// T result = null;
// for (T element : this stream) {
// if (!foundAny) {
// foundAny = true;
// result = element;
// }
// else
// result = accumulator.apply(result, element);
// }
// return foundAny ? Optional.of(result) : Optional.empty();
//accumulator函数必须是关联函数,但不限于顺序执行。
//这是一个terminal操作。
//参数:
accumulator 一个关联的、无干扰的、无状态的函数,用于组合两个值
//返回:
描述聚合结果的 Optional
//抛出:
NullPointerException – 如果归约结果为空
//也可以看看:
reduce(Object, BinaryOperator) , min(Comparator) , max(Comparator)
Optional<T> reduce(BinaryOperator<T> accumulator);
情况2
T reduce(T identity, BinaryOperator accumulator);
和情况1类似,但是多了一个T类型的初始值,这样在Stream为空时,也会返回初始值的值。
情况3
对于返回的值类型,和stream类型不一致,例如
1、accumulator 联合结果和下一个元素,结果类型为返回类型
2、并行计算时,accumulator 会产生多个结果,需要再提供一个方法,将结果类型 再联合起来
例如
int result=words.reduce
(
0,
(total,word)->total+word.length(),
(total_1,total_2)->total_1+total_2
);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
/**
* Returns an iterator for the elements of this stream.
*
* This is a terminal
* operation.
*
* @return the element iterator for this stream
*/
Iterator<T> iterator();
使用Stream.toArray() 方法 收集成 Object[]
使用Stream.toArray(IntFunction
//字符串 Stream
Stream<String> nameStream=Stream.of("大刘","小刘","小王","小王2","小李","小张");
//默认生成对象数组
Object[] objectArray=nameStream.toArray();
//生成指定类型的泛型数组,JAVA没办法new泛型的数组
// 所以利用IntFunction 这个函数式接口 R apply(int value); 用来快速创建对应类型的数组
//(r)->new String[r] 也可以换成方法引用 String[] :: new
String[] stringArray=nameStream.toArray((r)->new String[r]);
1. 创建目标对象类型实例的方法(目标对象不一定是集合,例如可以是StringBuilder)
1. 将元素添加到目标元素中的方法
1. 将两个对象整合到一起的方法 例如addAll
1、例如将流收集到List或者Set,使用Collectors.toSet() 或者自定义类型Collectors.toCollection(TreeSet::new)
ArrayList<String> arrayNameList=nameStream.collect(Collectors.toCollection(ArrayList::new));
List<String> nameList=nameStream.collect(Collectors.toList());
Set<String> nameSet=nameStream.collect(Collectors.toSet());
//如果需要返回其他类型,可以使用Collectors.toCollection(构造函数)
TreeSet<String> nameTreeSet=nameStream.collect(Collectors.toCollection(TreeSet::new));
2、将流作为字符串连接并收集 Collectors.joining()
//如果将字符串类型流进行拼接
String allString =nameStream.collect(Collectors.joining(","));
//将对象类型转化为字符串类型后再拼接
allString=nameStream.map(Object::toString).collect(Collectors.joining());
3、计算数值流的总和、平均值、最大值、最小值 summarziong(int|long|Double)
IntSummaryStatistics intSummaryStatistics=nameStream.collect(Collectors.summarizingInt(String::length));
intSummaryStatistics.getSum();
intSummaryStatistics.getMax();
intSummaryStatistics.getCount();
intSummaryStatistics.getMin();
intSummaryStatistics.getAverage();
4、遍历流
1. 使用forEach 但是对于并行流,该方法不能保证遍历的顺序
1. 使用forEachOrdered 可保证遍历顺序
5、将结果收集到Map中Collectors.toMap
Collectors.toMap有四个函数参数
1. 第一个是生成key
1. 第二个生成value
1. 第三个是key一样时对value的处理,**可以实现简单分组,更完整的分组,在下方第五节**
1. 第四个是对于Map有类型要求时的构造函数
//Collectors.toMap有四个函数参数,第一个是生成key,第二个生成value,第三个是key一样时对value的处理,第四个是对于Map有类型要求时的构造函数
//1、生成不同类型的value值
Map<String,Person> personMap=personStream.collect(Collectors.toMap(Person::getIdNumber,s->s));
personMap=personStream.collect(Collectors.toMap(Person::getIdNumber, Function.identity()));
Map<String,String> personIdNameMap=personStream.collect(Collectors.toMap(Person::getIdNumber,Person::getName));
//2、对于Key重复的值处理-1,如果不处理会抛出异常IllegalStateException,这里保留后面一个
personMap=personStream.collect(Collectors.toMap(Person::getIdNumber,Function.identity(),(existValue,newValue)->newValue));
//3、对于key重复的值处理-2收集成Set 或者List 作为Value的Map
(简单分组) Collections.singleton从一个对象生成了一个集合
Map<String,Set<String>> personIdNameList=personStream.collect(Collectors.toMap(Person::getIdNumber, l->Collections.singleton(l.getName()),(existvalue,newvalue)->{
Set<String> s=new HashSet<>(existvalue);
s.addAll(newvalue);
return s;
}));
//4、对于需要自定义返回Map类型,可以使用第四个参数来
HashMap<String,String> personIDMameMap2=personStream.collect(Collectors.toMap(Person::getIdNumber,Person::getName,(existValue,newValue)->newValue,HashMap::new));
//Collector.toMap 方法
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
Collectors.groupingBy()的三个参数,按照参数个数有三个重载方法
Collectors.groupingBy(
参数1:从元素类型中,取出分组的对象 ,
参数2:分组为Map,在此指定创建集成于Map具体容器,例如TreeMap,
参数3: 传入 Collector 对象,对分组结果的处理,相当于对子Stream的一次收集
)
//Collectors.groupingBy(
// 参数1:从元素类型中,取出分组的对象 ,
// 参数2:分组为Map,在此指定创建Map具体容器,例如TreeMap,
// 参数3: 传入 Collector 对象,对分组结果的处理,相当于对子Stream的一次收集)
//1、groupingBy()第一个参数 按照性别分组示例
Map<String, List<Person>> genderGroupListMap = personList.stream().collect(Collectors.groupingBy(p -> p.getGender()));
genderGroupListMap = personList.stream().collect(Collectors.groupingBy(Person::getGender));
//2、groupingBy 按照布尔分组时,使用partitioningBy比groupingBy更有效率
Map<Boolean, List<Person>> malePersonListMap = personList.stream().collect(Collectors.partitioningBy(s -> "男".equals(s.getGender())));
//3、Collectors.groupingByConcurrent() 会获得一个并发MAP,当流是并行流时,会并发地插入值。和toConcurrentMap类似
Map<Boolean, List<Person>> concurrentMap = personList.parallelStream().collect(Collectors.groupingByConcurrent(s -> "男".equals(s.getGender())));
//4、统计、求和、最大值最小值等操作
//4.1统计总条数 counting
Map<String, Long> genderCountMap = personList.stream().collect(Collectors.groupingBy(Person::getGender, Collectors.counting()));
//4.2统计总长度 summingInt
Map<String, Integer> nameLengthMap = personList.stream().collect(Collectors.groupingBy(Person::getGender, Collectors.summingInt(a -> a.getName().length())));
//4.3 求名字最长的
Map<String, Optional<Person>> maxNameLengthMap = personList.stream().collect(Collectors.groupingBy(Person::getGender, Collectors.maxBy(Comparator.comparing(s->s.name.length())) ));
maxNameLengthMap = personList.stream().collect(Collectors.groupingBy(Person::getGender, Collectors.minBy(Comparator.comparing(s->s.name.length())) ));
使用方式:
Collectors.groupingBy
(
生成用于分组的值函数,
mapping(参数1对downStream结果的元素进行处理 ,参数2 对分组的整个结果进行处理 )
)
示例:
//groupingBy 和Mapping,对分组结果里的元素进行转换再进行downStream操作
Map<String,Optional<String>> genderShortestNameMap=personList.stream().collect(
Collectors.groupingBy(
Person::getGender,
Collectors.mapping(
Person::getName,
Collectors.minBy(
Comparator.comparing(s->s.length())
)
)
)
);
//收集成Set
Map<String,Set<Person>> genderNameSetMap=personList.stream().collect(Collectors.groupingBy(Person::getGender,Collectors.toSet()));
//6、对于double int long 类型,可以收集成分析后的一些聚合分析结果,结果包含 最大值、最小值、平均数、总数、总和
TreeMap<String,IntSummaryStatistics> genderAgeStatistics=personList.stream().collect(
Collectors.groupingBy(
Person::getGender,TreeMap::new,Collectors.summarizingInt(Person::getAge)
)
);
三种形式,和3.2类似
downSteam收集器可以产生非常复杂的表达式,一般只在通过groupingBy或者 partitioningBy产生了 downSteam时,才使用他们。一般情况下只需要对流直接使用 map、reduce、count、max、min即可
类似于常见的Lambada函数,Stream流也有类似的原始类型流。避免了对基本类型流处理时重复包装造成效率低下。
对于原始类型,Stream Api 提供了,IntStream、LongStream、DoubleStream 原始流类型
流使得并行计算变得容易,他的处理过程几乎是自动的。
使用: 要使用并行流,需要生成一个并行流,默认情况创建Stream默认创建的是串行流,可以使用
说明:
正确的并行流操作,应当返回和串行流一致的操作结果。并且这些操作是无状态的。
需要保证传递给并行流执行的方法都是线程安全的
默认情况下,从有序集合、范围值、生成器、迭代器 或者Stream.sorted产生的流都是有序的,不考虑顺序时,一些操作可以更有效的运行。使用Stream.unordered方法可以不关心顺序,例如Stream.distinct就是可以从中获益。
map合并的开销很大,所以Collectors.groupingByConcurrent使用了共享的并发Map,将流标记为并行模式能更提高执行效率。