0.接口简述
Stream 的特性可以归纳为:
- 不是数据结构
- 它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
- 它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
- 所有 Stream 的操作必须以 lambda 表达式为参数
- 不支持索引访问
你可以请求第一个元素,但无法请求第二个,第三个,或最后一个 - 很容易生成数组或者 List
- 惰性化
- 很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
- Intermediate 操作永远是惰性化的。
- 并行能力
- 当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
- 可以是无限的
- 集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。
在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
常见的操作可以归类如下:
Intermediate:
一个流后面可以跟随零个或者多个Intermediate操作,其主要目的是打开流,做出某种程度的数据映射或者过滤,然后返回一个新的流,交给下一个操作使用,这类操作都是惰性化的,也就是说调用这类方法并没有真正开始流的遍历
- map (mapToInt, flatMap 等)、
- filter(filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream。)、
- distinct(依靠对象的toString()和hashCode()方法,对自定义类的对象去重一定要重写其中一个方法)、
- sorted(默认是升序排列)、
- peek(peek 对每个元素执行操作并返回一个新的 Stream)、
- parallel、
- sequential、
- unordered
Terminal:
一个流只能有一个terminal操作,当这个操作执行之后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作,terminal操作的执行才会真正开始流的遍历,并且会生成一个结果或者一个side effect。
- forEach(forEach 方法接收一个 Lambda 表达式,然后在 Stream 的每一个元素上执行该表达式)、
- forEachOrdered、
- toArray、
- reduce、
- collect、
- min、
- max、
- count、
- anyMatch(Stream 中只要有一个元素符合传入的 predicate,返回 true)、
- allMatch(Stream 中全部元素符合传入的 predicate,返回 true)、
- noneMatch(Stream 中没有一个元素符合传入的 predicate,返回 true)、
- findFirst(这是一个 termimal 兼 short-circuiting 操作,它总是返回 Stream 的第一个元素)、
- findAny、
- iterator
Short-circuiting:
- anyMatch、
- allMatch、
- noneMatch、
- findFirst、
- findAny、
- limit(limit 返回 Stream 的前面 n 个元素)
- skip(skip 跳过前 n 个元素)
注意: - limit与skip操作和sorted操作的使用次序、影响及性能、
- sorted之后 再执行limit与skip,无影响
1.创建Stream
public void buildStream() {
//1.Stream.of() 创建
Stream stream = Stream.of("a", "b", "c");
stream.forEach(System.out::println);
//2.基本类型对应的Stream创建,int -> IntStream , double -> DoubleStream
IntStream.of(new int[]{1, 2, 3, 4}).forEach(System.out::println);
IntStream.range(1, 4).forEach(System.out::println);
//3.最常用的用集合直接创建
List<String> list = Lists.newArrayList("a", "b", "c", "c");
list.stream();
}
output:
a
b
c
1
2
3
4
1
2
3
简单看一下Stream的类结构:
public interface Stream<T> extends BaseStream<T, Stream<T>> {...}
public interface IntStream extends BaseStream<Integer, IntStream> {...}
/**
* @param <T> the type of the stream elements
* @param <S> the type of of the stream implementing {@code BaseStream}
* @since 1.8
* @see Stream
* @see IntStream
* @see LongStream
* @see DoubleStream
* @see <a href="package-summary.html">java.util.stream</a>
*/
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {...}
2.流和集合(list、map、stack…)的转换
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
/**
* 辅助类
*/
private Long id;
private String name;
private Double grade;
}
public void transfrom() {
List<String> list = Lists.newArrayList("a", "b", "c", "c");
List<String> list1 = list.stream()
.collect(Collectors.toList());
System.out.println("list1:" + list1);
List<String> list2 = list.stream()
.collect(Collectors.toCollection(ArrayList::new));
System.out.println("list2:" + list2);
Set<String> set = list.stream()
.collect(Collectors.toSet());
System.out.println("set:" + set);
Stack<String> stack = list.stream()
.collect(Collectors.toCollection(Stack::new));
System.out.println("stack:" + stack);
String str = list.stream()
.collect(Collectors.joining()).toString();
System.out.println("str:" + str);
List<Student> students = Lists.newArrayList();
Student student1 = new Student(102L, "java", 93D);
Student student2 = new Student(103L, "python", 98.5);
Student student3 = new Student(101L, "go", 90D);
students.add(student1);
students.add(student2);
students.add(student3);
Map<Long, String> map = students.stream()
.collect(Collectors.toMap(Student::getId, Student::getName));
map.forEach((key, value) -> {
System.out.print(key + ":" + value + ", ");
});
}
output:
list1:[a, b, c, c]
list2:[a, b, c, c]
set:[a, b, c]
stack:[a, b, c, c]
str:abcc
101:go, 102:java, 103:python,
3.map、flatMap接口实例演示
public void mapTest() {
List<String> list = Lists.newArrayList("map", "flatMap", "stream");
List<String> list1 = list.stream()
.map(x -> {
String s = x.toUpperCase();
return s;
})
.collect(Collectors.toList());
System.out.println("list1:" + list1);
List<String> list2 = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("list2:" + list2);
List<String> list3 = list.stream()
.map(x -> x.toUpperCase())
.collect(Collectors.toList());
System.out.println("list3:" + list3);
//多个list拍平铺开,放进一个list中
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1),
Arrays.asList(2, 3),
Arrays.asList(4, 5, 6)
);
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());
outputStream.forEach(System.out::println);
//123456
}
output:
list1:[MAP, FLATMAP, STREAM]
list2:[MAP, FLATMAP, STREAM]
list3:[MAP, FLATMAP, STREAM]
1
2
3
4
5
6
注意:
List<String> list1 = list.stream().map(x -> {
x.toUpperCase();
return x;
}).collect(Collectors.toList());
不生效,因为流的属性以及map方法决定了其元素不会改变
将小写转换为大写的两种方式
- 双冒号:: -> 仅适用于会返回同种类型的方法,
或者说下例中Stream元素是String类型,toUpperCase()返回String,可以使用,contains()就不可以这么用 - lambda表达式,像例子中所展示的比较好理解
4.filter
public void filterTest() {
List<String> list = Lists.newArrayList("map",
" "/*五个空格*/, "flatMap", "filter", "java", "mybatis");
list.stream()
.filter(x -> x.length() > 4)
.forEach(System.out::println);
}
//挑选出长度大于4的
output:
(这是一个空行)
flatMap
filter
mybatis
5.findfirst
public void findFirst() {
List<String> list = Lists.newArrayList("c/c++", "java", "python", "java", "golong", "java");
Optional<String> first = list.stream()
.findFirst();
if (first.isPresent()) {
System.out.println("list first :" + first.get());
} else {
System.out.println("list no element!");
}
List<String> list1 = Lists.newArrayList();
Optional<String> first1 = list1.stream()
.findFirst();
if (first1.isPresent()) {
System.out.println("list1 first :" + first1.get());
} else {
System.out.println("list1 no element!");
}
}
output:
list first :c/c++
list1 no element!
6.sorted
public void sortedTest() {
List<String> list = Lists.newArrayList("c/c++", "java", "python", "java", "golong", "java");
list.stream()
.sorted()
.forEach(System.out::println);
//默认排序方式是升序!
List<Student> students = Lists.newArrayList();
Student student1 = new Student(102L, "java", 93D);
Student student2 = new Student(103L, "python", 98.5);
Student student3 = new Student(101L, "go", 90D);
students.add(student1);
students.add(student2);
students.add(student3);
// System.out.println("students:" + students);
//
// List<Student> collect = students.stream()
// .sorted()
// .collect(Collectors.toList());
// //报错 java.lang.ClassCastException
// System.out.println("after sorted by defult:" + collect);
List<Student> collect1 = students.stream()
.sorted(Comparator.comparing(Student::getGrade).reversed())
.collect(Collectors.toList());
System.out.println("after sorted by grade:" + collect1);
List<Student> collect2 = students.stream()
.sorted((x1, x2) -> x1.getId().compareTo(x2.getId()))
.collect(Collectors.toList());
System.out.println("after sorted by id:" + collect2);
}
output:
c/c++
golong
java
java
java
python
after sorted by grade:[Student(id=103, name=python, grade=98.5), Student(id=102, name=java, grade=93.0), Student(id=101, name=go, grade=90.0)]
after sorted by id:[Student(id=101, name=go, grade=90.0), Student(id=102, name=java, grade=93.0), Student(id=103, name=python, grade=98.5)]
7.distinct
public void distinctTest() {
List<Student> students = Lists.newArrayList();
Student student1 = new Student(102L, "java", 93D);
Student student2 = new Student(103L, "python", 98.5);
Student student3 = new Student(101L, "go", 90D);
Student student4 = new Student(103L, "python", 98.5);
Student student5 = new Student(101L, "go", 90D);
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
students.add(student5);
System.out.println("students:" + students);
List<Student> studentsList = students.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("after distinct:" + studentsList);
}
output:
students:[Student(id=102, name=java, grade=93.0), Student(id=103, name=python, grade=98.5), Student(id=101, name=go, grade=90.0), Student(id=103, name=python, grade=98.5), Student(id=101, name=go, grade=90.0)]
after distinct:[Student(id=102, name=java, grade=93.0), Student(id=103, name=python, grade=98.5), Student(id=101, name=go, grade=90.0)]
注意:
- 去重方法依据的是对象的toString()和hashCode()方法,来判定对象是否相等
- 本例中Student未实现这两个方法,但还是能够去重,这是因为继承自Object类自动实现的,并且成功的起到了判定依据,从上面的代码中可以看出, System.out.println(“students:” + students);的结果就是调用了自动实现的toString()方法,实际中不一定会生效,需要自己手动实现这两个方法
8.匹配
public void matchTest() {
List<String> list = Lists.newArrayList("c/c++", "javaee", "python", "javase", "golong", "javaleran");
System.out.println("list:" + list);
boolean isHasJava = list.stream()
.anyMatch(x -> x.contains("java"));
System.out.println("is Has Java ? :" + isHasJava);
boolean isAllLength = list.stream()
.allMatch(x -> x.length() > 4);
System.out.println("is All Length than 4 ? :" + isAllLength);
boolean isNoneReact = list.stream()
.noneMatch(x -> x.equals("react") || x.contains("react"));
System.out.println("is None React ? :" + isNoneReact);
}
output:
list:[c/c++, javaee, python, javase, golong, javaleran]
is Has Java ? :true
is All Length than 4 ? :true
is None React ? :true