Stream API简介
官方对Stream API给出的定义:A sequence of elemets supporting sequential and parallel aggregate operations。所以Java 8中引入的Stream API是一个用来处理数组和集合的API。Stream API具有如下的一些特性:
- Stream API不是数据结构,没有相关的内存存储。
- 不支持索引访问,只是对序列化的元素(数组和集合)进行处理
- 延迟计算,Stream API在进行终止操作之前不会开始计算。
- 方便的执行并行运行。
- 便捷的生成数组或集合。
- 支持过滤、查找、转换、聚合、汇总等针对流的操作。
Stream的运行机制包含三个部分,分别是:数据源source,中间操作和终止操作。流的数据源可以是一个数组、一个集合、一个生成器或一个I/O通道等。并且一个流可以由0个或者多个中间操作,每个中间操作都会返回一个新的流,以供下一个操作使用,一个流只能有一个终止操作。由于StreamAPI延迟计算的特点,因此stream只有在遇到终止操作时,才会执行stream的相关操作,在此之前并不会开始计算。
下面开始从Stream包含的数据源、中间操作和终止操作三个方面来对Stream API的使用进行举例说明。
Stream API使用详解
创建Stream源
Stream数据源(source)可以通过如下几种方式进行创建:
- 通过数据创建。
- 使用集合创建。
- 使用Stream.generate方法创建无限流。
- 使用Stream.iterate()方法创建无限流。
- 使用其他API创建,例如文件I/O等。
通过数组,使用Stream.of(array)创建流:
-
// 通过数组创建,使用Stream.of()方法
-
@Test
-
void generateDemo1() {
-
String[] array = {
"a",
"b",
"1",
"2"};
-
Stream<String> stream = Stream.of(array);
-
stream.forEach(System.out::println);
-
}
通过集合,使用.stream()方法创建流:
-
// 通过集合,list.stream()
-
@Test
-
void generateDemo2() {
-
List<String> list = Arrays.asList(
"a",
"b",
"1",
"2");
-
Stream<String> stream = list.stream();
-
stream.forEach(System.out::println);
-
}
通过Stream.generate()方法创建流:(因为该方法创建的为无限流,因此在输出时,需要使用limit方法来对流进行截取)
-
// 通过Stream.generate()方法
-
@Test
-
void generateDemo3() {
-
Stream<Integer> stream = Stream.generate(() ->
1);
// 这里是无限流,需要使用limit进行截取
-
stream.limit(
10)
// 使用limit方法获取最前面的10个数字
-
.forEach(System.out::println);
-
-
}
使用Stream.iterate()方法创建无限流,并使用limit方法截取流:
-
// 通过Stream.iterate()方法
-
@Test
-
void generateDemo4() {
-
Stream<Integer> stream = Stream.iterate(
1, x -> x +
1);
// 同样是无限流,需要使用limit进行截取
-
stream.limit(
10)
// 使用limit方法获取最前面的10个数字
-
.forEach(System.out::println);
-
-
}
通过其他API创建流,例如如下使用str.chars()方法可以创建String对象的IntStream流:
-
// 通过其他的API
-
@Test
-
void generateDemo5() {
-
String str =
"abcd1234567890";
-
IntStream stream = str.chars();
// 返回int类型的stream
-
// 使用方法的引用(终止操作进行输出)
-
stream.forEach(System.out::println);
// 等价于stream.forEach(x -> System.out.println(x));
-
}
通过文件I/O的方式来创建流,这里输出项目中pom.xml文件中的内容:
-
// 通过其他API(通过文件流)
-
@Test
-
void generateDemo6() throws IOException {
-
Files.lines(Paths.get(
"/Users/yitian/Documents/IDEAWorkspaces/LocalProjects/learning-project/pom.xml"))
-
.forEach(System.out::println);
-
}
流的终止操作
Stream流的终止操作常见的有如下几个:
- 循环:forEach
- 计算:min、max、count、average
- 查找:anyMatch、allMatch、noneMatch、findFirst、findAny
- 汇聚:reduce
- 收集:toArray、collect
当stream执行到终止操作时,才会真正的开始计算过程。下面为具体的方法实例:
-
// 终止操作forEach
-
@Test
-
void demo1() {
-
// 返回集合流中所有的偶数
-
Arrays.asList(
1,
2,
3,
4,
5).stream().filter(x -> {
-
System.out.println(
"----");
-
return x %
2 ==
0;
-
}).forEach(System.out::println);
// 如果没有终止操作,filter中间操作并不会进行,stream有延迟运行的特点
-
}
-
-
// 终止操作:map(), sum(), count(), get(), findAny(), findFirst()
-
@Test
-
void demo2() {
-
// 对集合中的元素,找到偶数然后进行求和
-
int sum = Arrays.asList(
1,
2,
3,
4,
5).stream()
-
.filter(x -> x %
2 ==
0)
-
.mapToInt(x -> x)
-
.sum();
-
System.out.println(sum);
-
-
// 计算集合中最大元素
-
int max = Arrays.asList(
1,
2,
3,
4,
5).stream()
-
.max((a, b) -> a - b)
-
.get();
-
System.out.println(max);
-
-
// 计算集合中最小元素
-
int min = Arrays.asList(
1,
2,
3,
4,
5).stream()
-
.min((a, b) -> a - b)
-
.get();
-
System.out.println(min);
-
-
// 查找偶数并计数
-
long count = Arrays.asList(
1,
2,
3,
4,
5).stream()
-
.filter(x -> x %
2 ==
0)
-
.count();
-
System.out.println(count);
-
-
// 查找偶数并返回任意一个
-
Optional<Integer> op1 = Arrays.asList(
1,
2,
3,
4,
5).stream()
-
.filter(x -> x %
2 ==
0)
-
.findAny();
-
System.out.println(op1.get());
-
-
// 查找偶数并返回第一个元素
-
Optional<Integer> op2 = Arrays.asList(
1,
2,
3,
4,
5).stream()
-
.filter(x -> x %
2 ==
0)
-
.findFirst();
-
System.out.println(op2.get());
-
}
-
-
// 终止操作:collect
-
@Test
-
void demo3() {
-
// 从1到50里面的所有偶数,放到一个list中
-
// Stream.iterate(1, x -> x + 1).limit(50).map(x -> x + " ").forEach(System.out::print);
-
List<Integer> list = Stream.iterate(
1, x -> x +
1)
-
.limit(
50)
-
.filter(x -> x %
2 ==
0)
-
.collect(Collectors.toList());
// 操作后生成一个List集合
-
list.stream()
-
.map(x -> x +
" ")
-
.forEach(System.out::print);
-
}
流的中间操作
Stream API允许0个或多个流的中间操作,常用的中间操作如下:
- 过滤:filter
- 去重:distinct
- 排序:sorted
- 截取:limit,skip
- 转换:map/flatMap
- 其他:peek
元素去重distinct或使用set:
-
// 中间操作:distinct
-
@Test
-
void demo4() {
-
// 去重
-
Arrays.asList(
1,
3,
4,
2,
2,
2,
5,
6,
7).stream()
-
.distinct()
-
.map(x -> x +
" ")
-
.forEach(System.out::print);
-
System.out.println();
-
-
// 使用set去重
-
Set<Integer> set = Arrays.asList(
1,
3,
4,
2,
2,
2,
5,
6,
7).stream()
-
.collect(Collectors.toSet());
-
set.stream().map(x -> x +
" ").forEach(System.out::print);
-
}
元素排序sort:
-
// 中间操作:sort()排序
-
@Test
-
void demo5() {
-
// 排序操作,默认为正序
-
Arrays.asList(
11,
2,
5,
1,
6,
8,
7).stream()
-
.sorted()
-
.forEach(System.out::print);
-
System.out.println();
-
-
// 这种也为正序
-
Arrays.asList(
11,
2,
5,
1,
6,
8,
7).stream()
-
.sorted((a, b) -> a - b)
-
.forEach(System.out::print);
-
System.out.println();
-
-
// 改为倒叙
-
Arrays.asList(
11,
2,
5,
1,
6,
8,
7).stream()
-
.sorted((a, b) -> b - a)
-
.forEach(System.out::print);
-
System.out.println();
-
-
// 字符串排序(按字符长度排序),并且为每个单词设置中间一个空格显示
-
Arrays.asList(
"cn",
"admin",
"net",
"io").stream()
-
.map(x -> x +
" ")
-
.sorted((a, b) -> a.length() - b.length())
-
.forEach(System.out::print);
-
System.out.println();
-
}
skip和limit可以实现分页的功能:
-
// 中间操作:skip
-
@Test
-
void demo6() {
-
// skip
-
List<Integer> list = Stream.iterate(
1, x -> x +
1)
-
.limit(
50)
-
.sorted((a, b) -> b - a)
// 从大到小排序
-
.skip(
10)
// skip为忽略前十个
-
.limit(
10)
-
.collect(Collectors.toList());
-
list.stream()
-
.map(x -> x +
" ")
-
.forEach(System.out::print);
-
// 40 39 38 37 36 35 34 33 32 31
-
-
// 使用使用skip实现分页
-
Stream.iterate(
1, x -> x +
1)
-
.limit(
50)
-
.skip(
0)
// 第一页: 40 39 38 37 36 35 34 33 32 31
-
.limit(
10)
-
.map(x -> x +
" ")
-
.forEach(System.out::print);
-
-
Stream.iterate(
1, x -> x +
1)
-
.limit(
50)
-
.skip(
10)
// 第二页:11 12 13 14 15 16 17 18 19 20
-
.limit(
10)
-
.map(x -> x +
" ")
-
.forEach(System.out::print);
-
}
转换map操作:
-
// 中间操作:map转换,mapToInt
-
@Test
-
void demo7() {
-
// 转换:将str进行分割,转换为整数并求和
-
String str =
"11,22,33,44,55,66";
-
int sum = Stream.of(str.split(
","))
-
.map(Integer::valueOf)
-
.mapToInt(x -> x)
-
.sum();
-
System.out.println(sum);
-
-
sum = Stream.of(str.split(
","))
-
.mapToInt(Integer::valueOf)
-
.sum();
-
System.out.println(sum);
-
}
-
-
// 中间操作:map转换为自定义对象
-
@Test
-
void demo8() {
-
String str =
"tomcat, nginx, apache, jetty";
-
// 将上面的字符串转换为4个User对象,下面三种方法等价
-
Stream.of(str.split(
", "))
-
.map(x ->
new User(x))
-
.forEach(System.out::println);
-
Stream.of(str.split(
", "))
-
.map(User::
new)
-
.forEach(System.out::println);
-
Stream.of(str.split(
", "))
-
.map(User::build)
-
.forEach(System.out::println);
-
}
-
-
static
class User {
-
private String name;
-
public User(String name) {
-
this.name = name;
-
}
-
-
public static User build(String name) {
-
User user =
new User(name);
-
return user;
-
}
-
-
public String getName() {
-
return name;
-
}
-
-
public void setName(String name) {
-
this.name = name;
-
}
-
-
@Override
-
public String toString() {
-
return
"User [name: "+name+
"]";
-
}
-
}
中间操作peek,用于返回中间的计算结果:
-
// 中间操作:peek
-
@Test
-
void demo9() {
-
// peek方法
-
String str =
"11,22,33,44,55,66";
-
int sum = Stream.of(str.split(
","))
-
.peek(x -> System.out.print(x +
" "))
-
.mapToInt(x -> Integer.valueOf(x))
-
.sum();
-
System.out.println(sum);
-
// 可以输出中间结果的计算值: 11 22 33 44 55 66 231
-
}
Stream的并行处理
Stream API除了上述具有的数据源、中间操作和终止操作外,在对集合或数据进行处理的过程中,还提供了并行计算的一个特性。使用Stream API可以很方便的对集合的处理使用多线程的方式进行,从而提高大型数据集合的处理效率。
在没有设置Stream API的并行计算时,其默认使用单线程的方式来进行运行,例如如下代码实例:
-
// 默认为一个main线程进行(同步)
-
@Test
-
void demo1() {
-
Optional<Integer> max = Stream.iterate(
1, x -> x +
1)
-
.limit(
200)
-
.peek(x -> {
-
System.out.println(Thread.currentThread().getName());
// 输出中间结果
-
}).max(Integer::compare);
-
System.out.println(max);
-
}
在对stream中元素求最大值时,使用peek方法输出中间结果(用于计算的线程名称),从输出可以看到所有的线程都是main线程一个线程在执行。
在Stream API中开启并行处理比较简单,直接使用.parallel()中间操作就就可以实现集合的并行处理,例如如下:
-
// 多线程计算
-
@Test
-
void demo2() {
-
Optional<Integer> max = Stream.iterate(
1, x -> x +
1)
-
.limit(
200)
-
.peek(x -> {
-
System.out.println(Thread.currentThread().getName());
// 输出中间结果
-
})
-
.parallel()
// 使用并行计算
-
.max(Integer::compare);
-
System.out.println(max);
-
}
此时在观察输出的线程名称,可以看到除main线程之外还包含很多其他的线程在运行,如下。可以看到默认使用了8个线程来进行集合的并行处理,这里是因为运行该程序的物理机的CPU为8个core,因此这里默认的线程并行数和CPU核心数保持一致。
-
ForkJoinPool
.commonPool-worker-7
-
ForkJoinPool
.commonPool-worker-6
-
ForkJoinPool
.commonPool-worker-1
-
ForkJoinPool
.commonPool-worker-2
-
ForkJoinPool
.commonPool-worker-5
-
ForkJoinPool
.commonPool-worker-4
-
ForkJoinPool
.commonPool-worker-3
-
main
上面加入parallel()中间操作时将流变成了并行流进行处理,其实并行流和序列流(sequence)是可以相互转换的,例如如下。此时在进行输出时,实际上还是只使用一个线程进行的集合处理:
-
// 顺序流和并行流之间的转换,因为stream为延迟计算,因此谁在最后面,则为优先级较高
-
@Test
-
void demo3() {
-
Optional<Integer> max = Stream.iterate(
1, x -> x +
1)
-
.limit(
200)
-
.peek(x -> {
-
System.out.println(Thread.currentThread().getName());
// 输出中间结果
-
})
-
.parallel()
// 设置为并行流
-
.sequential()
// 设置为顺序流
-
.max(Integer::compare);
-
System.out.println(max);
-
}
上面提到,加入parallel之后默认使用的并行线程数和CPU核心数保持一致,那么如果需要更改Stream处理的并行线程数,可以进行如下的设置:
- 一是在方法中设置系统变量来进行设置:System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "5");
- 或者使用-D的方式来配置JVM的启动参数也可以设置。
但这里的线程数建议还是不要超过CPU和核心数为宜,或者直接保持默认即可。
-
// Stream API内部是使用ForkJoinPool来实现的并行运行
-
// 设置并行运行的线程数时,一般和所在物理机的CPU核心数相一致
-
@Test
-
void demo4() {
-
// 使用设置启动参数的方式设置:-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
-
System.setProperty(
"java.util.concurrent.ForkJoinPool.common.parallelism",
"5");
-
Optional<Integer> max = Stream.iterate(
1, x -> x +
1)
-
.limit(
200)
-
.peek(x -> {
-
System.out.println(Thread.currentThread().getName());
-
}).parallel()
-
.max(Integer::compare);
-
System.out.println(max);
-
}
Java 8 Lambda表达式和Stream API使用实例
实例1:URL参数转换
对于类似于URL参数的字符串:"itemId=1&userId=10000&type=20&token=111111111111111&key=index"。将其根据参数名和参数值转换为Map集合:
-
@Test
-
void demo1() {
-
String queryString =
"itemId=1&userId=10000&type=20&token=111111111111111&key=index";
-
Map<String, String> paras = Stream.of(queryString.split(
"&"))
// 得到string[],每个元素为key=value
-
.map(x -> x.split(
"="))
// 对每个数组元素来进行分割,每个元素变为String[] = [key, value]
-
.collect(Collectors.toMap(s -> s[
0], s -> s[
1]));
// toMap方法将第1步转化的数组转化为map
-
System.out.println(paras);
-
// 输出结果:{itemId=1, type=20, userId=10000, key=index, token=111111111111111}
-
}
实例2:Book集合处理
Book对象类型如下:
-
public
class Book {
-
private
int id;
-
private String name;
-
private
double price;
-
private String type;
-
private LocalDate publishDate;
-
-
public Book() {
-
}
-
-
public Book(int id, String name, double price, String type, LocalDate publishDate) {
-
this.id = id;
-
this.name = name;
-
this.price = price;
-
this.type = type;
-
this.publishDate = publishDate;
-
}
-
-
@Override
-
public String toString() {
-
return
"Book{" +
-
"id=" + id +
-
", name='" + name +
'\'' +
-
", price=" + price +
-
", type='" + type +
'\'' +
-
", publishDate=" + publishDate +
-
'}';
-
}
-
-
// getter and setter
-
}
构建Book集合:
-
private List<Book> books() {
-
List<Book> books =
new ArrayList<>();
-
books.add(
new Book(
1,
"tomcat",
70d,
"服务器", LocalDate.parse(
"2014-05-17")));
-
books.add(
new Book(
2,
"jetty",
60d,
"服务器", LocalDate.parse(
"2015-12-01")));
-
books.add(
new Book(
3,
"nginx",
65d,
"服务器", LocalDate.parse(
"2016-10-17")));
-
books.add(
new Book(
4,
"java",
66d,
"编程语言", LocalDate.parse(
"2011-04-09")));
-
books.add(
new Book(
5,
"ruby",
80d,
"编程语言", LocalDate.parse(
"2013-05-09")));
-
books.add(
new Book(
6,
"php",
40d,
"编程语言", LocalDate.parse(
"2014-08-06")));
-
books.add(
new Book(
7,
"html",
44d,
"编程语言", LocalDate.parse(
"2011-01-06")));
-
books.add(
new Book(
8,
"oracle",
150d,
"数据库", LocalDate.parse(
"2013-08-09")));
-
books.add(
new Book(
9,
"mysql",
66d,
"数据库", LocalDate.parse(
"2015-04-06")));
-
books.add(
new Book(
10,
"ssh",
70d,
"编程语言", LocalDate.parse(
"2016-12-04")));
-
books.add(
new Book(
11,
"design pattern",
81d,
"软件工程", LocalDate.parse(
"2017-04-08")));
-
books.add(
new Book(
12,
"refactoring",
62d,
"软件工程", LocalDate.parse(
"2011-04-19")));
-
books.add(
new Book(
13,
"agile",
72d,
"软件工程", LocalDate.parse(
"2016-02-18")));
-
books.add(
new Book(
14,
"managing",
42d,
"软件工程", LocalDate.parse(
"2016-01-19")));
-
books.add(
new Book(
15,
"algorithm",
66d,
"软件工程", LocalDate.parse(
"2010-05-08")));
-
books.add(
new Book(
16,
"oracle 12c",
150d,
"数据库", LocalDate.parse(
"2016-05-08")));
-
return books;
-
}
处理1:将book集合中的所有的id取出来放到list的集合中
-
@Test
-
void demo2() {
-
List<Integer> ids1 = books().stream()
-
.map(book -> book.getId())
-
.collect(Collectors.toList());
-
System.out.println(ids1);
-
// 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
-
-
// 使用方法引用
-
List<Integer> ids2 = books().stream()
-
.map(Book::getId)
-
.collect(Collectors.toList());
-
System.out.println(ids2);
-
// 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
-
}
处理2:将book集合中的所有id取出来,使用逗号拼成一个字符串
-
@Test
-
void demo3() {
-
String str = books().stream()
-
.map(book -> book.getId() +
"")
-
.collect(Collectors.joining(
","));
-
System.out.println(str);
-
// 输出:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
-
-
str = books().stream()
-
.map(book -> book.getId() +
"")
-
.collect(Collectors.joining(
",",
"(",
")"));
// 逗号隔开并使用()括起来
-
System.out.println(str);
-
// 输出:(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
-
-
str = books().stream()
-
.map(book ->
"'"+book.getId()+
"'")
-
.collect(Collectors.joining(
","));
-
System.out.println(str);
-
// 输出:'1','2','3','4','5','6','7','8','9','10','11','12','13','14','15'
-
}
处理3:查找books中的所有类型
-
// 处理3:查找books中的所有类型
-
@Test
-
void demo4() {
-
// 输出所有类型
-
List<String> types = books().stream()
-
.map(book -> book.getType())
-
.collect(Collectors.toList());
-
System.out.println(types);
-
// 输出:[服务器, 服务器, 服务器, 编程语言, 编程语言, 编程语言, 编程语言, 数据库, 数据库, 编程语言, 软件工程, 软件工程, 软件工程, 软件工程, 软件工程]
-
-
types = books().stream()
-
.map(book -> book.getType())
-
.distinct()
// 使用distinct去重
-
.collect(Collectors.toList());
-
System.out.println(types);
-
// 输出:[服务器, 编程语言, 数据库, 软件工程]
-
-
Set<String> typeSet = books().stream()
-
.map(book -> book.getType())
-
.collect(Collectors.toSet());
// 使用Set集合去重
-
System.out.println(typeSet);
-
// 输出:[编程语言, 服务器, 软件工程, 数据库]
-
}
处理4: 对books集合进行排序(根据不同字段)
-
@Test
-
void demo5() {
-
// 1. 根据价格进行排序(升序排序)
-
books().stream()
-
.sorted((book1, book2) -> Double.compare(book1.getPrice(), book2.getPrice()))
-
.forEach(System.out::println);
-
System.out.println(
"---------------------");
-
// 等价于
-
Comparator<Book> comparator1 = (book1, book2) -> Double.compare(book1.getPrice(), book2.getPrice());
-
books().stream()
-
.sorted(comparator1)
-
.forEach(System.out::println);
-
System.out.println(
"---------------------");
-
// 输出:
-
// Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}
-
// Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19}
-
// Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06}
-
// Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01}
-
// Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19}
-
// Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}
-
// Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}
-
// Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}
-
// Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}
-
// Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}
-
// Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}
-
// Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18}
-
// Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}
-
// Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}
-
// Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}
-
// Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
-
-
// 2. 降序排序
-
Comparator<Book> comparator2 = (book1, book2) -> Double.compare(book2.getPrice(), book1.getPrice());
-
books().stream()
-
.sorted(comparator2)
-
.forEach(System.out::println);
-
System.out.println(
"---------------------");
-
// 等价于
-
books().stream()
-
.sorted(comparator1.reversed())
// 按照正序的烦序排序
-
.forEach(System.out::println);
-
System.out.println(
"---------------------");
-
// 输出
-
// Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}
-
// Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
-
// Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}
-
// Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}
-
// Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18}
-
// Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}
-
// Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}
-
// Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}
-
// Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}
-
// Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}
-
// Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}
-
// Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19}
-
// Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01}
-
// Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06}
-
// Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19}
-
// Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}
-
-
-
// 3. 多个属性值进行排序:先根据price排序(升序),price相同时根据publishDate进行排序(降序)
-
books().stream()
-
.sorted(comparator1.thenComparing(
-
(book1, book2) -> book1.getPublishDate().isAfter(book2.getPublishDate()) ? -
1 :
1)
-
)
-
.forEach(System.out::println);
-
// 输出
-
// Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}
-
// Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}
-
// Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}
-
-
// Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}
-
// Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}
-
}
对上述排序代码可以使用方法引用的方式进行简化:
-
@Test
-
void demo6() {
-
// 1. 根据价格进行排序
-
books().stream()
-
.sorted(Comparator.comparing(Book::getPrice))
-
.forEach(System.out::println);
-
-
// 2. 根据价格进行降序排序
-
books().stream()
-
.sorted(Comparator.comparing(Book::getPrice).reversed())
-
.forEach(System.out::println);
-
-
// 3. 先根据价格进行排序(升序),然后在根据publishDate进行排序(降序)
-
books().stream()
-
.sorted(Comparator.comparing(Book::getPrice).thenComparing(Comparator.comparing(Book::getPublishDate)).reversed())
-
.forEach(System.out::println);
-
}
处理5:将books集合转换为map<bookId, Book Object>
-
@Test
-
void demo7() {
-
Map<Integer, Book> bookMap = books().stream()
-
.collect(Collectors.toMap(Book::getId, book -> book));
// 直接使用了方法的引用
-
System.out.println(bookMap);
-
// 输出
-
// {
-
// 1=Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17},
-
// 2=Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01},
-
// 3=Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17},
-
// 4=Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09},
-
// 5=Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09},
-
// 6=Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06},
-
// 7=Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06},
-
// 8=Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09},
-
// 9=Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06},
-
// 10=Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04},
-
// 11=Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08},
-
// 12=Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19},
-
// 13=Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18},
-
// 14=Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19},
-
// 15=Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08},
-
// 16=Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
-
// }
-
}
处理6:统计集合中的平均值、最大值、和最小值
-
@Test
-
void demo8() {
-
// 1. 统计list中所有书的平均价格
-
Double averagePrice = books().stream()
-
.collect(Collectors.averagingDouble(Book::getPrice));
-
System.out.println(averagePrice);
-
// 输出:74.0
-
-
// 2. 找出价格最大或最小的书 (这里默认是找到最大的)
-
Optional<Book> book = books().stream()
-
.max(Comparator.comparing(Book::getPrice));
-
System.out.println(book);
-
// 等价于
-
book = books().stream()
-
.collect(Collectors.maxBy(Comparator.comparing(Book::getPrice)));
-
System.out.println(book);
-
// 输出:Optional[Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}]
-
-
// 3. 找到价格最小的Book
-
book = books().stream()
-
.collect(Collectors.maxBy(Comparator.comparing(Book::getPrice).reversed()));
-
System.out.println(book);
-
// 等价于
-
book = books().stream()
-
.collect(Collectors.minBy(Comparator.comparing(Book::getPrice)));
-
System.out.println(book);
-
// 输出:Optional[Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}]
-
-
// 4. 根据发布时间进行查找发布最晚的Book
-
book = books().stream()
-
.max(Comparator.comparing(Book::getPublishDate));
-
System.out.println(book);
-
// 输出:Optional[Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}]
-
-
// 5. 找到价格最贵的,但出版时间最晚的一本书
-
Comparator<Book> comparator = Comparator.comparing(Book::getPrice);
-
book = books().stream()
-
.collect(Collectors.maxBy(comparator.thenComparing(Comparator.comparing(Book::getPublishDate))));
-
System.out.println(book);
-
// 输出:Optional[Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}]
-
}
处理7:对集合中的元素进行分组统计
-
@Test
-
void demo9() {
-
// 1. 根据type进行分组,统计每个类别有多少本书
-
Map<String, List<Book>> booksMap = books().stream()
-
.collect(Collectors.groupingBy(Book::getType));
-
booksMap.keySet().forEach(type -> {
-
System.out.println(type);
-
System.out.println(booksMap.get(type));
-
System.out.println(
"--------------------");
-
});
-
// 编程语言
-
// [Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}, Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}, Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}, Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06}, Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}]
-
// --------------------
-
// 服务器
-
// [Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}, Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01}, Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}]
-
// --------------------
-
// 软件工程
-
// [Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}, Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19}, Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18}, Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19}, Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}]
-
// --------------------
-
// 数据库
-
// [Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}, Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}, Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}]
-
// --------------------
-
-
// 2. 统计每种类型数的数量
-
Map<String, Long> booksCount = books().stream()
-
.collect(Collectors.groupingBy(Book::getType, Collectors.counting()));
-
System.out.println(booksCount);
-
// {编程语言=5, 服务器=3, 软件工程=5, 数据库=3}
-
-
// 3. 统计每种类型书的总价格
-
Map<String, Double> priceMap = books().stream()
-
.collect(Collectors.groupingBy(Book::getType, Collectors.summingDouble(Book::getPrice)));
-
System.out.println(priceMap);
-
// {编程语言=300.0, 服务器=195.0, 软件工程=323.0, 数据库=366.0}
-
-
// 4. 统计每种类型的平均价格
-
Map<String, Double> averagePrice = books().stream()
-
.collect(Collectors.groupingBy(Book::getType, Collectors.averagingDouble(Book::getPrice)));
-
System.out.println(averagePrice);
-
// {编程语言=60.0, 服务器=65.0, 软件工程=64.6, 数据库=122.0}
-
-
// 5. 找到每种类型中最贵的书
-
Map<String, Optional<Book>> maxBooks = books().stream()
-
.collect(Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPrice))));
-
maxBooks.keySet().forEach(type -> {
-
System.out.println(type);
-
System.out.println(maxBooks.get(type));
-
System.out.println(
"--------------------");
-
});
-
// 编程语言
-
// Optional[Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}]
-
// --------------------
-
// 服务器
-
// Optional[Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}]
-
// --------------------
-
// 软件工程
-
// Optional[Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}]
-
// --------------------
-
// 数据库
-
// Optional[Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}]
-
// --------------------
-
-
// 6. 按类型查找每种类型中出版时间最晚的书
-
Map<String, Optional<Book>> publishBooks = books().stream()
-
.collect(Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPublishDate))));
-
publishBooks.keySet().forEach(type -> {
-
System.out.println(type);
-
System.out.println(publishBooks.get(type));
-
System.out.println(
"-------------------");
-
});
-
// 编程语言
-
// Optional[Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}]
-
// -------------------
-
// 服务器
-
// Optional[Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}]
-
// -------------------
-
// 软件工程
-
// Optional[Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}]
-
// -------------------
-
// 数据库
-
// Optional[Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}]
-
// -------------------
-
}
处理8:筛选book集合中price>90的书籍,并根据出版日期从近到远排序
-
@Test
-
void demo10() {
-
// 取出price>80,出版时间从近到远
-
books().stream()
-
.filter(book -> book.getPrice() >=
80)
-
.sorted(Comparator.comparing(Book::getPublishDate).reversed())
-
.forEach(System.out::println);
-
// Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}
-
// Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
-
// Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}
-
// Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}
-
}
实例3:交易记录数据处理
基础类结构
交易人员类对象:
-
public
class Trader {
-
private String name;
-
private String city;
-
-
public Trader(String name, String city) {
-
this.name = name;
-
this.city = city;
-
}
-
-
// getter and setter
-
}
交易对象结构:
-
public
class Transaction {
-
private Trader trader;
-
private
int year;
-
private
int value;
-
-
public Transaction(Trader trader, int year, int value) {
-
this.trader = trader;
-
this.year = year;
-
this.value = value;
-
}
-
}
数据构建和处理
根据上述的类来构建数据集合:
-
// build transaction list for query
-
private List<Transaction> transactions() {
-
Trader raoul =
new Trader(
"Raoul",
"Cambridge");
-
Trader mario =
new Trader(
"Mario",
"Milan");
-
Trader alan =
new Trader(
"Alan",
"Cambridge");
-
Trader brian =
new Trader(
"Brian",
"Cambridge");
-
-
List<Transaction> transactions = Arrays.asList(
-
new Transaction(brian,
2011,
300),
-
new Transaction(raoul,
2012,
1000),
-
new Transaction(raoul,
2011,
400),
-
new Transaction(mario,
2012,
710),
-
new Transaction(mario,
2012,
700),
-
new Transaction(alan,
2012,
950)
-
);
-
return transactions;
-
}
Query1: Find all transactions from year 2011 and sort them by value
-
@Test
-
public void query1() {
-
List<Transaction> tr2011 = transactions().stream()
-
.filter(transaction -> transaction.getYear() ==
2011)
-
.sorted(Comparator.comparing(Transaction::getValue))
-
.collect(Collectors.toList());
-
System.out.println(tr2011);
-
// [Transaction{trader=Trader{name='Brian', city='Cambridge'}, year=2011, value=300},
-
// Transaction{trader=Trader{name='Raoul', city='Cambridge'}, year=2011, value=400}]
-
}
Query2:What are all the unique cities where the traders work?
-
@Test
-
public void query2() {
-
List<String> cities = transactions().stream()
-
.map(transaction -> transaction.getTrader().getCity())
-
.distinct()
-
.collect(Collectors.toList());
-
System.out.println(cities);
-
// [Cambridge, Milan]
-
}
Query3: Find all traders from Cambridge and sort them by name
-
@Test
-
public void query3() {
-
List<Trader> traders = transactions().stream()
-
.map(Transaction::getTrader)
-
.filter(trader ->
"Cambridge".equals(trader.getCity()))
-
.distinct()
-
.sorted(Comparator.comparing(Trader::getName))
-
.collect(Collectors.toList());
-
System.out.println(traders);
-
// [Trader{name='Alan', city='Cambridge'}, Trader{name='Brian', city='Cambridge'}, Trader{name='Raoul', city='Cambridge'}]
-
}
Query4: Return a string of all traders' names sorted alphabetically (按照字母顺序排列的).
-
@Test
-
public void query4() {
-
String traderStr = transactions().stream()
-
.map(Transaction::getTrader)
-
.map(Trader::getName)
-
.distinct()
-
.sorted()
-
.reduce(
"", (name1, name2) -> name1 + name2);
-
System.out.println(traderStr);
-
// AlanBrianMarioRaoul
-
}
Query5: Are there any trader based in Milan?
-
@Test
-
public void query5() {
-
boolean milanBased = transactions().stream()
-
.anyMatch(transaction -> transaction.getTrader().getCity().equals(
"Milan"));
-
System.out.println(milanBased);
-
// true
-
}
Query6: Update all transactions so that the traders from Milan are set to Cambridge
-
public void query6() {
-
List<Transaction> transactions = transactions();
-
transactions.stream()
-
.map(Transaction::getTrader)
-
.filter(trader -> trader.getCity().equals(
"Milan"))
-
.forEach(trader -> trader.setCity(
"Cambridge"));
-
System.out.println(transactions);
-
// [Transaction{trader=Trader{name='Brian', city='Cambridge'}, year=2011, value=300},
-
// Transaction{trader=Trader{name='Raoul', city='Cambridge'}, year=2012, value=1000},
-
// Transaction{trader=Trader{name='Raoul', city='Cambridge'}, year=2011, value=400},
-
// Transaction{trader=Trader{name='Mario', city='Cambridge'}, year=2012, value=710},
-
// Transaction{trader=Trader{name='Mario', city='Cambridge'}, year=2012, value=700},
-
// Transaction{trader=Trader{name='Alan', city='Cambridge'}, year=2012, value=950}]
-
}
Query7: What's the highest value in all the transactions?
-
@Test
-
public void query7() {
-
int highestValue1 = transactions().stream()
-
.map(Transaction::getValue)
-
.reduce(
0, Integer::max);
-
-
int highestValue2 = transactions().stream()
-
.map(Transaction::getValue)
-
.max(Comparator.comparingInt(a -> a))
-
.get();
-
System.out.println(highestValue1);
-
System.out.println(highestValue2);
-
// 1000
-
// 1000
-
}
THE END.
转载:https://blog.csdn.net/yitian_z/article/details/104680964