1 流的创建
1.1 流的创建方法
创建流的方法有三种,分别是:Stream.of()、Stream.iterate()、Stream.generate(),然后,分别看一下这三个方法的声明。
static <T> Stream<T> of(T... values)
//Stream.of():参数很简单,就是一系列的泛型参数。
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
//Stream.iterate():第一个参数是一个初始值,第二个参数是一个操作。
static <T> Stream<T> generate(Supplier<T> s)
//Stream.generate():参数就是一个Supplier的供给型的参数。
1.2 流的创建方法举例
@Test
public void testCreateStream() {
//利用Stream.of方法创建流
Stream<String> stream = Stream.of("hello", "world", "Java8");
stream.forEach(System.out::println);
System.out.println("##################");
//利用Stream.iterate方法创建流
Stream.iterate(10, n -> n + 1).limit(5).collect(Collectors.toList()).forEach(System.out::println);
System.out.println("##################");
//利用Stream.generate方法创建流
Stream.generate(Math::random).limit(5).forEach(System.out::println);
System.out.println("##################");
//从现有的集合中创建流
List<String> strings = Arrays.asList("hello", "world", "Java8");
String string = strings.stream().collect(Collectors.joining(","));
System.out.println(string);
}
2 流的操作
2.1 装箱流
double流想要转换为 List,有 3 种比较好的解决方法.
利用 boxed 方法
利用 boxed 方法,可以将 DoubleStream 转换为 Stream ,例如;
DoubleStream.of(1.0, 2.0, 3.0)
.boxed()
.collect(Collectors.toList());
利用 mapToObj 方法
利用 mapToObj 方法也可以实现上面的功能,另外,也提供了 mapToInt、mapToLong、mapToDouble 等方法将基本类型流转换为相关包装类型。
DoubleStream.of(1.0, 2.0, 3.0)
.mapToObj(Double::valueOf)
.collect(Collectors.toList());
collect 方法
一般情况下,我们利用 collect 方法的时候,都是用于将流的数据收集为基本类型的集合,例如;
stream.collect(Collectors.toList())
然而,collect 方法其实还有一种更加一般化的形式,如下;
<R> R collect(Supplier<R> supplier,
ObjIntConsumer<R> accumulator,
BiCnsumer<R,R> combiner)
第一个参数是一个供给器,相当于初始化一个容器,
第二个参数是累加器,相当于给初始化的容器赋值,
第三个参数是组合器,相当于将这些元素全部组合到一个容器。
List<Double> list = DoubleStream.of(1.0, 2.0, 3.0)
.collect(ArrayList<Double>::new, ArrayList::add, ArrayList::addAll);
第一个参数:使用一个静态方法初始化一个 List 容器,
第二个参数:使用静态方法 add ,添加元素,
第三个参数:使用静态方法 addAll ,用于联合所有的元素。
2.2 字符串与流之间的转换
String 转为流有两种方法,分别是 java.lang.CharSequence 接口定义的默认方法 chars 和 codePoints
@Test
public void testString2Stream() {
String s = "hello world Java8".codePoints()//转换成流
.collect(StringBuffer::new,
StringBuffer::appendCodePoint,
StringBuffer::append)//将流转换为字符串
.toString();
String s1 = "hello world Java8".chars()//转换成流
.collect(StringBuffer::new,
StringBuffer::appendCodePoint,
StringBuffer::append)//将流转换为字符串
.toString();
}
2.3 流的映射 map 与 flatMap
需要将一个集合的对象的某一个字段取出来,然后再存到另外一个集合中
Java8 之前实现
@Test
public void testList() {
List<Person> list = new ArrayList<>();
List<Friend> friends = new ArrayList<>();
friends.add(new Friend("Java5"));
friends.add(new Friend("Java6"));
friends.add(new Friend("Java7"));
Person person = new Person();
person.setFriends(friends);
list.add(person);
List<String> strings = new ArrayList<>();
for(Person p : list){
strings.add(p.getName());
}
}
Java8 使用 map 和 flatMap
<R> Stream<R> map(Function<? super T,? extends R> mapper)
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
@Test
public void testMapAndFlatMap() {
List<Person> list = new ArrayList<>();
List<Friend> friends = new ArrayList<>();
friends.add(new Friend("Java5"));
friends.add(new Friend("Java6"));
friends.add(new Friend("Java7"));
Person person = new Person();
person.setFriends(friends);
list.add(person);
//映射出名字
List<String> strings = list.stream().map(Person::getName).collect(Collectors.toList());
//map 方法,参数给定 Person::getName 映射出 name,然后再用 collect 收集到 List 中
//映射出朋友
List<List<Friend>> collect = list.stream().map(Person::getFriends).collect(Collectors.toList());
//返回值是 List<List<Friend>>,集合里面还包着集合,处理有点麻烦
List<Friend> collect1 = list.stream().flatMap(friend -> friend.getFriends().stream()).collect(Collectors.toList());
//atMap 的方法能够“展平”包裹的流,这就是 map 和 flatMap 的区别。
}
2.4 流的连接
流的连接有两种方式,如果是两个流的连接,使用 Stream.concat 方法,如果是三个及三个以上的流的连接,就使用 Stream.flatMap 方法。
@Test
public void testConcatStream() {
//两个流的连接
Stream<String> first = Stream.of("sihai", "sihai2", "sihai3");
Stream<String> second = Stream.of("sihai4", "sihai5", "sihai6");
Stream<String> third = Stream.of("siha7", "sihai8", "sihai9");
Stream<String> concat = Stream.concat(first, second);
//多个流的连接
Stream<String> stringStream = Stream.of(first, second, third).flatMap(Function.identity());
}
3 流的规约操作
3.1 内置的规约操作
基本类型流都有内置的规约操作。包括average、count、max、min、sum、summaryStatistics,前面的几个方法相信不用说了,summaryStatistics 方法是前面的几个方法的结合,下面我们看看他们如何使用。
@Test
public void testReduce1() {
String[] strings = {"hello", "sihai", "hello", "Java8"};
long count = Arrays.stream(strings)
.map(String::length)
.count();
System.out.println(count);//4
System.out.println("##################");
int sum = Arrays.stream(strings)
.mapToInt(String::length)
.sum();
System.out.println(sum);//20
System.out.println("##################");
OptionalDouble average = Arrays.stream(strings)
.mapToInt(String::length)
.average();
System.out.println(average);//OptionalDouble[5.0]
System.out.println("##################");
OptionalInt max = Arrays.stream(strings)
.mapToInt(String::length)
.max();
System.out.println(max);//OptionalInt[5]
System.out.println("##################");
OptionalInt min = Arrays.stream(strings)
.mapToInt(String::length)
.min();
System.out.println(min);//OptionalInt[5]
DoubleSummaryStatistics statistics = DoubleStream.generate(Math::random)
.limit(1000)
.summaryStatistics();
System.out.println(statistics);//DoubleSummaryStatistics{count=1000, sum=490.322996, min=0.000175, average=0.490323, max=0.999745}
}
3.2 基本的规约操作
基本的规约操作是利用前面讲过的 reduce 方法实现的,IntStream 接口定义了三种 reduce 方法的重载形式,如下;
OptionalInt reduce(IntBinaryOperator op)
int reduce(int identity, IntBianryOperator op)
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BianryOperator<U> combiner)
identity 参数就是初始化值的意思,
IntBianryOperator 类型的参数就是操作,例如 lambda 表达式;
BianryOperator<U> combiner是一个组合器,在前面有讲过。
@Test
public void testReduce2() {
int sum = IntStream.range(1, 20)
.reduce((x, y) -> x + y)
.orElse(0);
System.out.println(sum);//190
System.out.println("##################");
int sum2 = IntStream.range(1, 20)
.reduce(0, (x, y) -> x + 2 * y);
System.out.println(sum2);//380
System.out.println("##################");
int sum3 = IntStream.range(1, 20)
.reduce(0, Integer::sum);
System.out.println(sum3);//190
}
第一个是1到20累加的操作,第二个以0为初始值,然后2倍累加,第三个是以0为初始值,累加。
3.3 流的计数
流的数量统计有两种方法,分别是 Stream.count() 方法和 Collectors.counting() 方法。
@Test
public void testStatistics() {
//统计数量
String[] strings = {"hello", "sihai", "hello", "Java8"};
long count = Arrays.stream(strings)
.count();
System.out.println(count);
System.out.println("##################");
Long count2 = Arrays.stream(strings)
.collect(Collectors.counting());
System.out.println(count2);
}
4 流的查找与匹配
4.1 流的查找
流的查找 Stream 接口提供了两个方法 findFirst 和 findAny。
findFirst 方法返回流中的第一个元素的 Optional,而 findAny 方法返回流中的某个元素的 Optional。
String[] strings = {"hello", "sihai", "hello", "Java8"};
Optional<String> first = Arrays.stream(strings).findFirst();
System.out.println(first.get());
System.out.println("##################");
Optional<String> any = Arrays.stream(strings).findAny();
System.out.println(any.get());
System.out.println("##################");
4.2 流的匹配
流的匹配 Stream 接口提供了三个方法,分别是 anyMatch(任何一个元素匹配,返回 true)、allMatch(所有元素匹配,返回 true)、noneMatch(没有一个元素匹配,返回 true)。
boolean b = Stream.of(1, 2, 3, 4, 5, 10)
.anyMatch(x -> x > 5);
System.out.println(b);
System.out.println("##################");
boolean b2 = Stream.of(1, 2, 3, 4, 5, 10)
.allMatch(x -> x > 5);
System.out.println(b2);
System.out.println("##################");
boolean b3 = Stream.of(1, 2, 3, 4, 5, 10)
.noneMatch(x -> x > 5);
System.out.println(b3);
5 流的总结
流的创建方法。
流的系列操作,包括装箱流、字符串与流之间的转换、流和映射 map 和 flatMap、流的连接。
流的规约操作
流的查找与匹配
作者:欧阳思海
链接:http://blog.ouyangsihai.cn/java8-de-stream-liu-shi-cao-zuo-zhi-wang-zhe-gui-lai.html
来源:ouyangsihai.cn