一、Stream
public interface Stream<T> extends BaseStream<T, Stream<T>>
支持顺序和并行聚合操作的元素序列
可能是数组、集合、生成器函数、I/O通道等
流是惰性的;只有在启动终端操作时才对源数据执行计算,并且仅根据需要使用源元素。
1、与I/O流不同,它更像具有Iterable的集合类,但行为和集合类又有所不同
2、Stream是对集合功能的增强,它专注于对集合中的对象进行非常便利、高效的聚合操作,或者大量数据操作
3、只要给出需要对其包含的元素执行什么操作,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
4、 Java中的Stream并不会存储元素,而是按需计算。
- 并行数据处理
在Java 7之前,处理并行数据集合非常麻烦,首先需要将一个庞大数据集合分成几个子集合;然后需要为每一个子集合编写多线程处理程序,还需要对他们做线程同步来避免访问共享变量导致处理结果不准确;最后,等待所有线程处理完毕后将处理结果合并。在Java 7之后新添加了一个fork/join
的框架,让这一切变得更加简单。
创建流
1. Stream类
1.1 of(T)
- 创建流
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
1.2 of(T…)
- 创建流
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
1.3 empty()
- 返回一个空的顺序Stream,该Stream里面不包含元素项。
public static<T> Stream<T> empty() {
return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
}
1.4 concat
- 将两个Stream合并成一个Stream
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
1.5 generate
- 返回无限流
public static<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
案例
//通过生成器产生5个10以内的随机数,如果不使用limit就会无限生成10以内随机数
Stream.generate(() -> Math.random() * 10).limit(5).forEach(System.out::println);
----------输出--------
0.8320556195819129
6.260534125204207
7.344094646332503
0.18490598959698068
6.392272744710005
generate()方法用于生成一些随机数,比如生成10个UUID
Stream.generate(() -> UUID.randomUUID().toString()).limit(10).forEach(System.out::println);
1.6 iterate
- 返回无限流,用于生成一系列值
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
案例:
打印100以内的所有偶数
Stream.iterate(0, n -> n + 2).limit(51).forEach(System.out::println);
一般来说,iterate()用于生成一系列值,比如生成以当前时间开始之后的10天的日期
Stream.iterate(LocalDate.now(), date -> date.plusDays(1)).limit(10).forEach(System.out::println);
2. Collection类
-
顺序流
new ArrayList<String>().stream();
-
并行流
new ArrayList<String>().parallelStream();
- Java内部会将流的内容分割成若干个子部分,然后将它们交给多个线程并行处理,这样就将工作的负担交给多核CPU的其他内核处理。
并行流案例:
接受一个数字n作为参数,返回从1到n的所有自然数之和 public static long sequentialSum(long n) { return Stream.iterate(1L, i -> i + 1) .limit(n) .reduce(0L, Long::sum); }
iterate()
方法不适合用并行流处理 ,生成的对象是基本类型的包装类(也就是java.lang.Long
类型),必须进行拆箱操作才能运算。会导致效率很低
{
public static long parallelSum1(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.reduce(0L, Long::sum);
}
public static long parallelSum2(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.parallel()
.reduce(0L, Long::sum);
}
public static long parallelSum3(long n) {
return LongStream.rangeClosed(1, n)
.parallel()
.reduce(0L, Long::sum);
}
public static void main(String[] args) {
long start1 = System.currentTimeMillis();
long number = 10000000L;
// parallelSum1(number);//200+
// parallelSum2(number);//2000+----》并行处理,但是存在自动装箱拆箱的问题,所以很慢
parallelSum3(number);//75+
long stop1 = System.currentTimeMillis();
System.out.println("Parallel Sum: " + (stop1 - start1) + " 毫秒");
}
}
3. Arrays
- Arrays.Stream()也可以获取一个数组流
中间操作
1. filter
Stream<T> filter(Predicate<? super T> predicate);
- 从流中排除某些元素
案例
List<Integer> list = Arrays.asList(1,2,3,523,21,55);
list.stream().filter(x -> x > 10).forEach(System.out::println);
2. distinct
Stream<T> distinct();
- 去重
List<Integer> list = Arrays.asList(1,2,3,3,2,4);
Stream<Integer> stream3 = list.stream().distinct();
stream3.forEach(System.out::println);
3.sorted
- 排序
3.1sorted()
Stream<T> sorted();
- 自然排序 按照Comparable的方式
案例
List<String> list = Arrays.asList("aa","cc","bb");
Stream<String> stream3 = list.stream().sorted();
stream3.forEach(System.out::println);
3.2sorted(Comparator)
Stream<T> sorted(Comparator<? super T> comparator);
- 定制排序
案例
List<Integer> list = Arrays.asList(1,3,2,6,8,3,9);
Stream<Integer> stream3 = list.stream().sorted(Integer::compare);
stream3.forEach(System.out::println);
4. peek
Stream<T> peek(Consumer<? super T> action);
- 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数
- peek接收一个没有返回值的λ表达式,可以做一些输出,外部处理等
注意:仅在对流内元素进行操作时,peek才会被调用,当不对元素做任何操作时,peek自然也不会被调用了
该方法主要用于调试,方便debug查看Stream内进行处理的每个元素
5. limit
Stream<T> limit(long maxSize);
- 截断流,使其元素不超过给定数量
案例
List<Integer> list = Arrays.asList(1,2,3,523,21,55);
Stream<Integer> stream3 = list.stream().limit(3);
stream3.forEach(System.out::println);
6. skip
Stream<T> skip(long n);
- 跳过元素返回一个抛弃了前n个元素的流,若流中元素不满足n个,则返回一个空流,与limit形成互补
案例:
List<Integer> list = Arrays.asList(1,2,3,523,21,55);
Stream<Integer> stream3 = list.stream().skip(3);
stream3.forEach(System.out::println);
7. map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
- 应用到每个元素上,并将其映射成一个新的元素。
案例
//map()里面使用函数型接口(Function)
List<String> list = Arrays.asList("aa","bb","cc");
Stream<String> stream3 = list.stream().map(String::toUpperCase);
stream3.forEach(System.out::println);
System.out.println(list);
----------------------输出-----------------------
AA
BB
CC
[aa, bb, cc]
------------------------------------------------
集合里的每一个元素都会使用到String.toUpperCase()方法
它是以aa作为一个元素,bb作为一个元素
获取索引
public static int checkIdNumber(String number) {
Optional<Integer> first = arrayList.stream().map(x -> {
if (x.getIdNumber().equalsIgnoreCase(number)) {
return arrayList.indexOf(x);
}
return -1;
}).filter(x -> x != -1).findFirst();
if (first.isPresent()) {
return first.get();
}
return -1;
}
8. flatMap
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
- 将流中的每个值都换成另一个流,然后把所有流连接一个流
flatMap将原来的流转换为一个新的流并且,是以每一个值为单位的
案例:
List<String> list = Arrays.asList("aa","bb","cc");
Stream<String> stream3 = list.stream().flatMap(l -> {
String[] strings = l.split("");
return Arrays.stream(strings);
});
stream3.forEach(System.out::println);
-------------------输出-----------
a
a
b
b
c
c
将多个Stream连接成一个Stream,这时候不是用新值取代Stream的值,与map有所区别,这是重新生成一个Stream对象取而代之。
9. 不常用
- mapToInt
- mapToIong
- mapToDouble
- flatMapToInt
- flatMapToIong
- flatMapToDouble
终止操作
1. forEach
void forEach(Consumer<? super T> action);
- 迭代流中的每个数据
Stream<String> stream=Stream.of("wa1","wa2");
stream.forEach(System.out::println);
2. forEachOrdered
如果流有顺序,则用这个,同forEach一样
3. toArray
Object[] toArray();
- 将流转换为数组
- Integer[] integers = stream.toArray(Integer[]::new);
4.* reduce
三个
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);
将流中的元素进行合并,形成一个新的值
常见的归约操作包括求和,求最大值或最小值
归约操作一般使用reduce()
方法,与map()
方法搭配使用,可以处理一些很复杂的归约操作。
案例:
Stream<String> stream = Stream.of("w","a");
System.out.println(stream.reduce((x,y)->x+y).get());
// 获取流
List<Book> books = Arrays.asList(
new Book("Java编程思想", "Bruce Eckel", "机械工业出版社", 108.00D),
new Book("Java 8实战", "Mario Fusco", "人民邮电出版社", 79.00D),
new Book("MongoDB权威指南(第2版)", "Kristina Chodorow", "人民邮电出版社", 69.00D)
);
// 计算所有图书的总价
Optional<Double> totalPrice = books.stream()
.map(Book::getPrice)
.reduce((n, m) -> n + m);
5.* collect
2个
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
对流的汇总操作
- 生成一个新的list、set、hashSet
Stream.collect(Collectors.toList());
Stream.collect(Collectors.toSet());
Stream.collect(Collectors.toCollection(HashSet::new));
- 元素总数
.collect(Collectors.counting());
- 获取平均值
.collect(Collectors.averagingDouble(User::getSalary));
- 获取总和
.collect(Collectors.summingDouble(User::getSalary));
- 获取最大值或最小值
.collect(Collectors.maxBy(Comparator.comparingDouble(User::getSalary))); .collect(Collectors.minBy(Comparator.comparingDouble(User::getSalary)));
- 分组
.collect(Collectors.groupingBy(User::getSalary));
- 连接
.collect(Collectors.joining("--"));
- 分区
Map<Boolean, List<User>> collect1 = user.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 3000));
System.out.println(collect1);
-----------------输出--------------
{false=[User{name='张三', age=12, salary=1000.0}], true=[User{name='李四', age=32, salary=4000.0}, User{name='王五', age=40, salary=4000.0}, User{name='王五', age=40, salary=4000.0}]}
- 多级分组
Map<Double, Map<String, List<User>>> collect = user.stream().collect(Collectors.groupingBy(User::getSalary, Collectors.groupingBy(
u -> {
if ( u.getAge() <= 12) {
return "青年";
} else if ( u.getAge() <= 32) {
return "中年";
} else {
return "老年";
}
}
)));
System.out.println(collect);
-----------------输出--------------
{4000.0={老年=[User{name='王五', age=40, salary=4000.0}, User{name='王五', age=40, salary=4000.0}], 中年=[User{name='李四', age=32, salary=4000.0}]}, 1000.0={青年=[User{name='张三', age=12, salary=1000.0}]}}
6. min
Optional<T> min(Comparator<? super T> comparator);
求最小值
7. max
Optional<T> max(Comparator<? super T> comparator);
求最大值
8. count
long count();
求总和
9. anyMatch
boolean anyMatch(Predicate<? super T> predicate);
检查流中的任意元素是否满足条件,满足就true
检查是否有匹配至少一个元素
10. allMatch
boolean allMatch(Predicate<? super T> predicate);
检查流中的所有元素是否满足条件,满足就true,一个不满足就false
检查是否所有元素都满足要求
11. noneMatch
boolean noneMatch(Predicate<? super T> predicate);
检查流中的所有元素是否满足条件,满足就false,一个不满足就true
检查是否没有匹配的元素
12. findFirst
Optional<T> findFirst();
查找第一个元素
13. findAny
Optional<T> findAny();
查找任意元素
实际上测试结果发现,findFirst()
和findAny()
返回的都是第一个元素,那么两者之间到底有什么区别?通过查看javadoc描述,大致意思是findAny()
是为了提高并行操作时的性能,如果没有特别需要,还是建议使用findAny()
方法。
常见的转换
{
List<Integer> list = Arrays.asList(1, 3, 4, 2, 14, 52, 45, 2);
System.out.println(list);
Stream<Integer> stream = list.stream();
//转换为Object的数组
// Object[] objects = stream.toArray();
//转化为Integer的数组
// Integer[] integers = stream.toArray(Integer[]::new);
//转化为List
// List<Integer> collect = stream.distinct().collect(Collectors.toList());
// System.out.println(collect);
//转化为hashset
// HashSet<Integer> collect1 = stream.collect(Collectors.toCollection(HashSet::new));
// System.out.println(collect1);
// stream.collect(Collectors.toCollection(ArrayList::new));
//转化为String
// stream.map(String::valueOf).collect(Collectors.joining()).toString();
//转化为String[]
String[] strings = stream.map(s -> s + "asd").toArray(String[]::new);
System.out.println(strings.toString());
stream.close();
//永远不会改变原始数据
System.out.println(list);
}