Java(十四)
一、Stream流
1.1流式思想概述
注意:请暂时忘记对传统IO流的固有印象! 整体来看,流式思想类似于工厂车间的“生产流水线”。
当需要对多个元素进行操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该首先拼好一个“模型”步骤 方案,然后再按照方案去执行它。
Stream(流)是一个来自数据源的元素队列 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。 数据源 流的来源。 可以是集合,数组 等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭 代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。
1.2 Stream方法
Filter过滤
具体实现代码如下:
import org.w3c.dom.ls.LSOutput;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/*
使用Stream流的方式,遍历集合,对集合中的数据进行过滤
Stream流是JDK1.8之后出现的
关注的是做什么,而不是怎么做
*/
public class Demo01Stream {
public static void main(String[] args) {
//创建一个List集合,存储姓名
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
//对List集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
//遍历集合
Stream<String> stream= list.stream()
.filter(name->name.startsWith("张"))
.filter(name -> name.length() == 3);
stream.forEach(name -> System.out.println(name));
/*
Stream流属于管道流,只能被消费(使用)一次
第一个Stream流调用完毕,数据就会流转到下一个Stream上
而这时第一个Stream流已经使用完毕,就会关闭了
所以第一个Stream流就不能再调用方法了
IllegalStateException: stream has already been operated upon or closed
*/
//遍历stream流
//stream.forEach(name-> System.out.println(name));
}
}
Map映射
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
Function中的抽象方法:
R apply(T t);
具体代码实现如下:
import java.util.stream.Stream;
/*
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
Function中的抽象方法:
R apply(T t);
*/
public class Demo02StreamMap {
public static void main(String[] args) {
//获取一个String类型的Stream流
Stream<String> stream = Stream.of("1", "2","3", "4");
//使用map方法,把字符串类型的整数,转换(映射)为Integer类型的整数
Stream<Integer> map = stream.map((String s)->{
return Integer.parseInt(s);
});
//遍历Stream流
map.forEach(i-> System.out.println(i));
}
}
Count统计
Stream流中的常用方法:用于同济Stream流中元素的个数
long count();
count方法是一个终结方法,返回值是一个龙类型的整数
所以不能在继续调用Stream流中的其他方法了
具体代码实现如下:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/*
Stream流中的常用方法:用于同济Stream流中元素的个数
long count();
count方法是一个终结方法,返回值是一个龙类型的整数
所以不能在继续调用Stream流中的其他方法了
*/
public class Demo03StreamCount {
public static void main(String[] args) {
//获取一个Stream流
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
list.add(8);
list.add(9);
Stream<Integer> stream = list.stream();
long count = stream.count();
System.out.println(count);
}
}
Concat 组合
Stream流中的常用方法,concat:用于把流组合到一起
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
具体代码实现如下:
import java.util.stream.Stream;
/*
Stream流中的常用方法,concat:用于把流组合到一起
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
*/
public class StreamConcat {
public static void main(String[] args) {
//获取一个Stream流
Stream<String> stream1 = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
//获取一个Stream流
String[] arr = {"aaa", "bbb", "ccc","ddd","eee"};
Stream<String> stream2 = Stream.of(arr);
//把以上两个流转为一个流
Stream<String> concat = Stream.concat(stream1, stream2);
//遍历concat流
concat.forEach(name-> System.out.println(name));
}
}
Limit 限定长度
limit 方法可以对流进行截取,只取用前n个。方法签名:
Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。
limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法
具体代码实现如下:
/*
limit 方法可以对流进行截取,只取用前n个。方法签名:
Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。
limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法
*/
import java.util.stream.Stream;
public class StreamLimit {
public static void main(String[] args) {
//获取一个Stream流
String[] arr = {"aaa", "bbb", "ccc"};
Stream<String> stream = Stream.of(arr);
//使用limit对Stream流中的元素进行截取,只要前三个元素
Stream<String> stream2 = stream.limit(3);
//遍历stream流
stream2.forEach(name-> System.out.println(name));
}
}
Skip跳过
Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
具体实现代码如下:
import java.util.stream.Stream;
/*
Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
*/
public class StreamSkip {
public static void main(String[] args) {
//获取一个Stream流
String[] arr = {"aaa", "bbb", "ccc","ddd","eee"};
Stream<String> stream = Stream.of(arr);
//使用skip方法跳过前三个元素
Stream<String> stream1 = stream.skip(3);
//遍历stream2流
stream1.forEach(name -> System.out.println(name));
}
}
1.3 Stream流的综合应用
先有一道题目:
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以 下若干操作步骤:
1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
5. 将两个队伍合并为一个队伍;存储到一个新集合中。
6. 根据姓名创建 Person 对象;存储到一个新集合中。
7. 打印整个队伍的Person对象信息。
需要通过Stream流来实现:
具体代码实现如下:
1.定义Person类
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
2.测试主体类具体实现
import java.util.ArrayList;
import java.util.stream.Stream;
/*
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以 下若干操作步骤:
1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
5. 将两个队伍合并为一个队伍;存储到一个新集合中。
6. 根据姓名创建 Person 对象;存储到一个新集合中。
7. 打印整个队伍的Person对象信息。
*/
public class Demo01StreamTest {
public static void main(String[] args) {
//第一支队伍
ArrayList<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("石破天");
one.add("石中玉");
one.add("老子");
one.add("庄子");
one.add("洪七公");
//1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
//2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
Stream<String> stream = one.stream().filter(name->name.length() == 3).limit(3);
//第二支队伍
ArrayList<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("赵丽颖");
two.add("张三丰");
two.add("尼古拉斯赵四");
two.add("张天爱");
two.add("张二狗");
//3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
//4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
Stream<String> stream1 = two.stream().filter(name->name.startsWith("张")).skip(2);
//5. 将两个队伍合并为一个队伍;存储到一个新集合中。
Stream<String> newStream = Stream.concat(stream, stream1);
//6. 根据姓名创建 Person 对象;存储到一个新集合中。
Stream<Person> personStream = newStream.map((String name)->{
return new Person(name);
});
//7. 打印整个队伍的Person对象信息。
personStream.forEach(person -> System.out.println(person));
}
}