package cn.cuit.testa0;
import static java.util.stream.Collectors.averagingDouble;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.Spliterator;
import java.util.Stack;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.Range;
/**
* 参考文章: 搜索java Stream API查看更多文章
* https://www.cnblogs.com/CarpenterLee/p/6545321.html
* https://www.cnblogs.com/CarpenterLee/p/6550212.html
* https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html
*
*
* 当把一个数据结构包装成 Stream 后,就要开始对里面的元素进行各类操作了。常见的操作可以归类如下。
*
* Intermediate中间操作: map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、
* limit、 skip、 parallel、 sequential、 unordered
*
* Terminal终结操作: forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、
* count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
*
* Short-circuiting短路操作: anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、
* limit
*
* java8 四大核心函数式接口Function、Consumer、Supplier、Predicate
* https://blog.csdn.net/smithallenyu/article/details/80653897
*
* Function
*
* T:入参类型,R:出参类型
*
* 调用方法:R apply(T t);
*
* 定义函数示例:Function func = p -> p * 10; // 输出入参的10倍
*
* 调用函数示例:func.apply(10); // 结果100
*
* Consumer
*
* T:入参类型;没有出参
*
* 调用方法:void accept(T t);
*
* 定义函数示例:Consumer consumer= p -> System.out.println(p); //
* 因为没有出参,常用于打印、发送短信等消费动作
*
* 调用函数示例:consumer.accept("18800008888");
*
* Supplier
*
* T:出参类型;没有入参
*
* 调用方法:T get();
*
* 定义函数示例:Supplier supplier= () -> 100; //
* 常用于业务“有条件运行”时,符合条件再调用获取结果的应用场景;运行结果须提前定义,但不运行。
*
* 调用函数示例:supplier.get();
*
* Predicate
*
* T:入参类型;出参类型是Boolean, 所以Boolean表达式就用这个函数式接口
*
* 调用方法:boolean test(T t);
*
* 定义函数示例:Predicate predicate = p -> p % 2 == 0; // 判断是否、是不是偶数
*
* 调用函数示例:predicate.test(100); // 运行结果true
*
* Collector接受三个泛型参数,对可变减少操作的数据类型作相应限制:
*
* T:输入元素类型 A:缩减操作的可变累积类型(通常隐藏为实现细节) R:可变减少操作的结果类型
*
* UnaryOperator UnaryOperator 接收T对象,返回T对象,是Function的特例化,本质上也是Function型的函数式接口
*
* BinaryOperator BinaryOperator 接收两个T对象,返回T对象
*
* 原则:遇到操作或者生产集合类型及数组容器,都可以想到可以使用Stream操作,并非要刻意去使用,简 单易懂的代码才是好代码
*/
public class Java8StreanAPI
{
// @Test
// public void testTakeWhileAndDropWhile() {
/**
* 从Stream中依次获取满足条件的元素,直到不满足条件为止结束获取
*
* 举例:Stream中的元素 12, 4, 3, 6, 8, 9
*
* 条件是 x -> x % 2 == 0 ,即判断是否为偶数,即当遇到元素不为偶数时终止获取
*
* 那么得到的结果输出就是,12, 4 因为下一个元素为3不为偶数,即结束获取,丢弃后面的其他元素
*/
// IntStream.of(12,4,3,6,8,9).takeWhile(x->x%2==0).forEach(System.out::print);
/**
* 从Stream中依次删除满足条件的元素,直到不满足条件为止结束删除
*
* 举例:Stream中的元素 12, 4, 3, 6, 8, 9
*
* 条件是 x -> x % 2 == 0 ,即判断是否为偶数,即当遇到元素不为偶数时终止删除
*
* 那么得到的结果输出就是,3, 6, 8, 9 因为下一个元素为3不为偶数,即结束删除,返回3及以后剩下的所有元素
*/
// IntStream.of(12,4,3,6,8,9).dropWhile(x->x%2==0).forEach(System.out::print);
// }
// public void testIO(String source, String target) {
// Java7后自动关闭流,把需要关闭的资源声明再try的小括号里,Java10后的类型推断var
// try (var in = new FileInputStream(source); var out = new
// FileOutputStream(target)) {
// Java9后,文件复制
// in.transferTo(out);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
/**
* 改进的 Stream API
*
* 在 Java 9 中它会变得更好。Stream 接口中添加了 4 个新的方法:dropWhile, takeWhile, ofNullable。还有个
* iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代
*
* Optional 和 Stream 之间的结合也得到了改进。现在可以通过 Optional 的新方法 stram将一个 Optional
* 对象转换为一个(可能是空的) Stream 对象,在组合复杂的 Stream 管道时,将 Optional 转换为 Stream 非常有用。
*/
@Test
public void test03()
{
// 向控制台打印 1 到 99
// IntStream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
// Optional转换为Stream
// Stream s = Optional.of(1).stream();
// s.forEach(System.out::println);
}
public static List transactions = new ArrayList<>();
public static Transaction transaction = null;
static
{
for (int i = 0; i < 10; i++ )
{
if (new Random().nextBoolean())
{
transaction = new Transaction(i, "JAVA8", "JDK8-Stream");
transactions.add(transaction);
}
else
{
transaction = new Transaction(i, "JAVA7", "JDK7-Non-Stream");
transactions.add(transaction);
}
}
}
/**
* 在 Java 7 中,如果要发现 type 为 grocery 的所有Transaction,然后返回以Transaction降序排序好的交易 ID
* 集合,我们需要这样写:
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
public void testInJ7()
{
List groceryTransactions = new ArrayList<>();
for (Transaction t : transactions)
{
if (t.getType() == Transaction.GROCERY)
{
groceryTransactions.add(t);
}
}
Collections.sort(groceryTransactions, new Comparator()
{
public int compare(Object t1, Object t2)
{
return ((Transaction)t2).getValue().compareTo(((Transaction)t1).getValue());
}
});
List transactionsIds = new ArrayList<>();
for (Transaction t : groceryTransactions)
{
transactionsIds.add(t.getId());
}
transactionsIds.forEach(System.out::print);
}
/**
* 对比上面的Java 7代码,在 Java 8 中,使用Stream API首先不说其他的,代码量就少了一般,而且看起来也更加优雅,默认升序
*/
@Test
public void testInJ8()
{
List transactionsIds = transactions.parallelStream().filter(t -> t.getType() == Transaction.GROCERY).sorted(
Comparator.comparing(Transaction::getValue).reversed()).map(Transaction::getId).collect(Collectors.toList());
transactionsIds.forEach(System.out::print);
transactions.stream().filter(e -> e != null).filter(e -> e.getId() == 1).findAny().get();
transactions.stream().filter(Objects::nonNull).filter(Java8StreanAPI.idIs1()).findAny().get();
transactions.stream().filter(Objects::nonNull).filter(Java8StreanAPI::idIs101).findAny().get();
}
public static Predicate idIs1()
{
return e -> e.getId() == 1;
}
public static boolean idIs101(Transaction e)
{
return e.getId() == 1;
}
/* 构造流的方式很多,下面是的几种常见方法 */
@SuppressWarnings({"unused", "rawtypes"})
@Test
public void testCreateStream()
{
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String[] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List list = Arrays.asList(strArray);
stream = list.stream();
// 需要注意的是,对于基本数值型,目前有三种对应的包装类型 Stream:
// IntStream、LongStream、DoubleStream
// 数值流的构造
IntStream.of(new int[] {1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);
// 流转换为其它数据结构
// 一个 Stream 只可以使用一次,下面的代码为了简洁而重复使用了数次
// 1. Array
String[] strArray1 = (String[])stream.toArray(String[]::new);
// 2. Collection
List list1 = (List)stream.collect(Collectors.toList());
List list2 = (List)stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = (Set)stream.collect(Collectors.toSet());
Stack stack1 = (Stack)stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();
}
@SuppressWarnings("unused")
@Test
public void testMapWithFlatMap()
{
List nums = Arrays.asList(1, 2, 3, 4);
List squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());
squareNums.forEach(System.out::print);
// 一对多,flatMap 把 input Stream 中的层级结构扁平化【就是把多个集合整合成一个集合】,就是将最底层元素抽出来放到一起,最终
// output 的新 Stream
// 里面已经没有 List 了,都是直接的数字。
/**
* Stream -> flatMap -> Stream
* Stream> -> flatMap -> Stream
* Stream> -> flatMap -> Stream
* Stream> -> flatMap -> Stream
*
* 也就是Stream里面是集合而不是单体对象,当时集合的时候,Stream的操作(filter,sum,distinct
* ...)和collectors不支持它,所以使用FlatMap可以整合成一个几个单体对象的Stream,就可以了
*/
Stream> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
Stream outputStream = inputStream.flatMap(childList -> childList.stream());
Stream outputStream1 = inputStream.flatMap(List::stream); // 等价上面,数组也可以Arrays::stream
outputStream.forEach(System.out::print);
// Optional optional = Optional.ofNullable(Integer.valueOf(0));
}
// map/flatMap它的作用就是把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素
@SuppressWarnings({"unused", "resource"})
@Test
public void testMapAndfFlatMap()
throws IOException
{
List wordList = new ArrayList<>();
wordList.add("abcd");
// 转换大写
List output = wordList.stream().map(String::toUpperCase).collect(Collectors.toList());
List output01 = wordList.stream().map(String::toUpperCase).collect(Collectors.toCollection(ArrayList::new));
output.forEach(System.out::println);
output01.forEach(System.out::println);
// 这段代码生成一个整数 list 的平方数 {1, 4, 9, 16}
/**
* 从上面例子可以看出,map 生成的是个 1:1 映射,每个输入元素, 都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要
* flatMap
*/
List nums = Arrays.asList(1, 2, 3, 4);
List squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());
squareNums.forEach(System.out::println);
// 一对多,flatMap 把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起【多个集合变一个集合】,最终 output 的新
// Stream
// 里面已经没有 List 了,都是直接的数字。
Stream> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
Stream outputStream = inputStream.flatMap((childList) -> childList.stream());
outputStream.forEach(System.out::println);
// filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream
// 留下偶数
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens = Stream.of(sixNums).filter(n -> n % 2 == 0).toArray(Integer[]::new);
// 把单词挑出来
// 这段代码首先把每行的单词用 flatMap 整理到新的 Stream,然后保留长度不为 0 的,就是整篇文章中的全部单词了
String REGEXP = ",";
BufferedReader reader = new BufferedReader(new FileReader(new File("test.txt")));
List outputWords = reader.lines().flatMap(line -> Stream.of(line.split(REGEXP))).filter(word -> word.length() > 0).collect(Collectors.toList());
// 当需要为多核系统优化时,可以
// parallelStream().forEach(),只是此时原有元素的次序没法保证,并行的情况下将改变串行时操作的行为,此时 forEach
// 本身的实现不需要调整,而 Java8 以前的 for 循环 code 可能需要加入额外的多线程逻辑
// 但一般认为,forEach 和常规 for 循环的差异不涉及到性能,它们仅仅是函数式风格与传统 Java 风格的差别
// forEach 不能修改自己包含的本地变量值,也不能用 break/return 之类的关键字提前结束循环。
// peek 对每个元素执行操作并返回一个新的 Stream
Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("Filtered value: " + e)).map(String::toUpperCase).peek(
e -> System.out.println("Mapped value: " + e)).collect(Collectors.toList());
// findFirst
// 这是一个 termimal 兼 short-circuiting 操作,它总是返回 Stream 的第一个元素,或者空
// 这里比较重点的是它的返回值类型:Optional。这也是一个模仿 Scala,在Java9后Optional可以和Stream互相转换
// 语言中的概念,作为一个容器,它可能含有某值,或者不包含。使用它的目的是尽可能避免 NullPointerException
/**
* 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream
* 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如
* Stream 的 sum 就相当于
*
* Integer sum = integers.reduce(0, (a, b) -> a+b); 或
*
* Integer sum = integers.reduce(0, Integer::sum);
*
* 也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。
* BinaryOperator接口,可以看到reduce方法接受一个函数,这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素。这个方法返回值类型是Optional
*
* 第二个变形,与第一种变形相同的是都会接受一个BinaryOperator函数接口,不同的是其会接受一个identity参数,用来指定Stream循环的初始值。如果Stream为空,就直接返回该值。另一方面,该方法不会返回Optional,因为该方法不会出现null
*
* reduce前两种变形,因为接受参数不同,其执行的操作也有相应变化:
*
* 变形1,未定义初始值,从而第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素
* 变形2,定义了初始值,从而第一次执行的时候第一个参数的值是初始值,第二个参数是Stream的第一个元素
*/
// 代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为
// BinaryOperator,即操作运行规则。这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的
// reduce(),由于可能没有足够的元素,返回的是 Optional,请留意这个区别
// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
// 对 Stream 的排序通过 sorted 进行,它比数组的排序更强之处在于你可以首先对 Stream 进行各类
// map、filter、limit、skip 甚至 distinct 来减少元素数量后,再排序,这能帮助程序明显缩短执行时间。我们对清单 14 进行优化:
List persons = new ArrayList();
for (int i = 1; i <= 5; i++ )
{
Person person = new Person(i, "name" + i, 0);
persons.add(person);
}
List personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());
System.out.println(personList2);
/**
* min/max/distinct
*
* min 和 max 的功能也可以通过对 Stream 元素先排序,再 findFirst 来实现,但前者的性能会更好,为 O(n),而 sorted
* 的成本是 O(n log n)。同时它们作为特殊的 reduce 方法被独立出来也是因为求最大最小值是很常见的操作
*/
// /找出最长一行的长度
BufferedReader br = new BufferedReader(new FileReader("c:\\SUService.log"));
int longest = br.lines().mapToInt(String::length).max().getAsInt();
br.close();
System.out.println(longest);
// 下面的例子则使用 distinct 来找出不重复的单词。
// 找出全文的单词,转小写,并排序
List words = br.lines().flatMap(line -> Stream.of(line.split(" "))).filter(word -> word.length() > 0).map(String::toLowerCase).distinct().sorted().collect(
Collectors.toList());
br.close();
System.out.println(words);
/**
* Stream 有三个 match 方法,从语义上说:
*
* allMatch:Stream 中全部元素符合传入的 predicate,返回 true anyMatch:Stream 中只要有一个元素符合传入的
* predicate,返回 true noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
*
* 它们都不是要遍历全部元素才能返回结果。例如 allMatch 只要一个元素不满足条件,就 skip 剩下的所有元素,返回 false。对清单 13 中的
* Person 类稍做修改,加入一个 age 属性和 getAge 方法。
*/
List persons01 = new ArrayList();
persons01.add(new Person(1, "name" + 1, 10));
persons01.add(new Person(2, "name" + 2, 21));
persons01.add(new Person(3, "name" + 3, 34));
persons01.add(new Person(4, "name" + 4, 6));
persons01.add(new Person(5, "name" + 5, 55));
boolean isAllAdult = persons.stream().allMatch(p -> p.getAge() > 18);
System.out.println("All are adult? " + isAllAdult);
boolean isThereAnyChild = persons.stream().anyMatch(p -> p.getAge() < 12);
System.out.println("Any child? " + isThereAnyChild);
}
// limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素(它是由一个叫 subStream 的方法改名而来)
public void testLimitAndSkip()
{
List persons = new ArrayList();
for (int i = 1; i <= 10000; i++ )
{
Person person = new Person(i, "name" + i, 0);
persons.add(person);
}
List personList2 = persons.stream().map(Person::getName).limit(10).skip(3).collect(Collectors.toList());
System.out.println(personList2);
}
// 这是一个有 10,000 个元素的 Stream,但在 short-circuiting 操作 limit 和 skip 的作用下,管道中 map
// 操作指定的 getName() 方法的执行次数为 limit 所限定的 10 次,而最终返回结果在跳过前 3 个元素后只有后面 7 个返回。
// 有一种情况是 limit/skip 无法达到 short-circuiting 目的的,就是把它们放在 Stream 的排序操作后,
// 原因跟 sorted
// 这个 intermediate 操作有关:此时系统并不知道 Stream 排序后的次序如何,
// 所以 sorted
// 中的操作看上去就像完全没有被 limit
// 或者 skip 一样
// limit 和 skip 对 sorted 后的运行次数无影响
/**
* 即虽然最后的返回元素数量是 2,但整个管道中的 sorted 表达式执行次数没有像前面例子相应减少。
*
* 最后有一点需要注意的是,对一个 parallel 的 Steam 管道来说,如果其元素是有序的,那么 limit
* 操作的成本会比较大,因为它的返回对象必须是前 n 个也有一样次序的元素。取而代之的策略是取消元素间的次序,或者不要用 parallel Stream
*/
@Test
public void testLimitAndSkipNo()
{
List persons = new ArrayList();
for (int i = 1; i <= 5; i++ )
{
Person person = new Person(i, "name" + i, 0);
persons.add(person);
}
List personList2 = persons.stream().sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).limit(2).collect(Collectors.toList());
// 当集合中存在null元素时,可以使用针对null友好的比较器,null元素排在集合的最前面
List persons2 = new ArrayList();
IntStream.range(0, 5).forEach(i -> {
Person person = new Person(i, "name" + i, 0);
persons2.add(person);
});
persons2.add(null);
List personList3 = persons2.stream().sorted(Comparator.nullsFirst(Comparator.comparing(Person::getAge).thenComparing(Person::getName))
// 参数 keyComparator ,keyComparator 是创建一个自定义的比较器
.thenComparing((x, y) -> {
return new StringBuilder(x.getName()).reverse().toString().compareToIgnoreCase(y.getName());
})).limit(2).collect(Collectors.toCollection(ArrayList::new));
System.out.println(personList2);
personList3.forEach(System.out::println);
}
class Person
{
public int no;
private String name;
private int age;
public Person(int no, String name, int age)
{
this.no = no;
this.name = name;
this.age = age;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getName()
{
System.out.println(name);
return name;
}
@Override
public String toString()
{
return "Person [no=" + no + ", name=" + name + ", age=" + age + "]";
}
}
// Optional 的两个用例,Java8的时候Stream可以转换为Optional,而Optional不能转为Stream,Java8以后,就可以互转了
// optional.stream()//optional 可以转为stream
@Test
public void testOptional()
{
String strA = " abcd ", strB = null;
print(strA);
print("");
print(strB);
getLength(strA);
getLength("");
getLength(strB);
}
public static void print(String text)
{
// Java 8
Optional.ofNullable(text).ifPresent(System.out::println);
BigDecimal positiveSum = null;
positiveSum = Optional. ofNullable(positiveSum).orElse(BigDecimal.ZERO);
// Pre-Java 8
if (text != null)
{
System.out.println(text);
}
if (positiveSum == null)
{
positiveSum = BigDecimal.ZERO;
}
}
public static int getLength(String text)
{
// Java 8
return Optional.ofNullable(text).map(String::length).orElse(-1);
// Pre-Java 8
// return if (text != null) ? text.length() : -1;
};
// 进阶:自己生成流
/**
* Stream.generate
*
* 通过实现 Supplier 接口,你可以自己来控制流的生成。这种情形通常用于随机数、常量的 Stream,或者需要前后元素间维持着某种状态信息的
* Stream。把 Supplier 实例传递给 Stream.generate() 生成的 Stream,默认是串行(相对 parallel
* 而言)但无序的(相对 ordered 而言)。由于它是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Test
public void testCreateSelfStream()
{
// 生成 10 个随机整数
Random seed = new Random();
Supplier random = seed::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
Stream.generate(seed::nextInt).limit(10).forEach(System.out::println);
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
seed.ints(10).forEach(System.out::println);
// Another way
IntStream.generate(() -> (int)(System.nanoTime() % 100)).limit(10).forEach(System.out::println);
// Stream.generate() 还接受自己实现的 Supplier。例如在构造海量测试数据的时候,用某种自动的规则给每一个变量赋值;或者依据公式计算
// Stream 的每个元素值。这些都是维持状态信息的情形
// 自实现 Supplier
Stream.generate(new PersonSupplier()).limit(10).forEach(p -> System.out.println(p.getName() + ", " + p.getAge()));
/**
* Stream.iterate
*
* iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。然后种子值成为 Stream
* 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推
*/
// 生成一个等差数列,与 Stream.generate 相仿,在 iterate 时候管道必须有 limit 这样的操作来限制 Stream 大小
Stream.iterate(0, n -> n + 3).limit(10).forEach(x -> System.out.print(x + " "));
/**
* 进阶:用 Collectors 来进行 reduction 操作 java.util.stream.Collectors
* 类的主要作用就是辅助进行各类有用的 reduction 操作,例如转变输出为 Collection,把 Stream 元素进行归组。
*
* groupingBy/partitioningBy
*/
// 按照年龄归组
Map> personGroups = Stream.generate(new PersonSupplier()).limit(100).collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext())
{
Map.Entry> persons = (Map.Entry>)it.next();
System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
}
// 上面的 code,首先生成 100 人的信息,然后按照年龄归组,相同年龄的人放到同一个 list 中
// 按照未成年人和成年人归组
Map> children = Stream.generate(new PersonSupplier()).limit(100).collect(Collectors.partitioningBy(p -> p.getAge() < 18));
System.out.println("Children number: " + children.get(true).size());
System.out.println("Adult number: " + children.get(false).size());
/**
* 在使用条件“年龄小于 18”进行分组后可以看到,不到 18 岁的未成年人是一组,成年人是另外一组。partitioningBy 其实是一种特殊的
* groupingBy,它依照条件测试的是否两种结果来构造返回的数据结构,get(true) 和 get(false) 能即为全部的元素对象
*/
}
/**
* 前面的章节我们提及到过双冒号运算符,双冒号运算就是Java中的[方法引用],[方法引用]的格式是
*
* 类名::方法名 , 这种式子一般是用作Lambda表达式,Lambda有所谓懒加载嘛 表达式:
*
* person -> person.getAge();
*
* 可以替换成
*
* Person::getAge
*
* 表达式
*
* () -> new HashMap<>();
*
* 可以替换成
*
* HashMap::new
*
* 这种[方法引用]或者说[双冒号运算]对应的参数类型是Function T表示传入类型,R表示返回类型。比如表达式person ->
* person.getAge();
* 传入参数是person,返回值是person.getAge(),那么方法引用Person::getAge就对应着Function类型
*
*/
@Test
public void convertTest()
{
List collected01 = new ArrayList<>();
collected01.add("alpha");
collected01.add("beta");
collected01 = collected01.stream().map(string -> string.toUpperCase()).collect(Collectors.toList());
System.out.println(collected01);
List collected02 = new ArrayList<>();
collected02.add("alpha");
collected02.add("beta");
collected02 = collected02.stream().map(String::toUpperCase).collect(Collectors.toCollection(ArrayList::new));// 注意发生的变化
System.out.println(collected02);
}
private class PersonSupplier implements Supplier
{
private int index = 0;
private Random random = new Random();
@Override
public Person get()
{
return new Person(index++ , "StormTestUser" + index, random.nextInt(100));
}
}
// ============================追加,根据4大函数式接口,设计一些简单的自己的函数接口和函数式编程==============================
@Test
public void testIsPrime()
{
System.out.println(isPrime(1));
System.out.println(isPrime(2));
System.out.println(isPrime(3));
System.out.println(isPrime(4));
System.out.println(isPrime01(1));
System.out.println(isPrime01(2));
System.out.println(isPrime01(3));
System.out.println(isPrime01(4));
System.out.println(isPrimeByBoolean(4));
}
// 判断质数
public boolean isPrime(final int number)
{
return number > 1 && IntStream.range(2, number).noneMatch(element -> number % element == 0); // noneMatch(IntPredicate)
}
// 判断质数
public boolean isPrimeByBoolean(final int number)
{
return Boolean.logicalAnd(number > 1, IntStream.range(2, number).noneMatch(element -> number % element == 0));
}
// 判断质数改写
public static boolean isPrime01(final int number)
{
IntPredicate predicate = element -> number % element == 0;
return number > 1 && IntStream.range(2, number).noneMatch(predicate); // noneMatch(IntPredicate)
}
// 从一个数字集合中找到第一个大于3的数,并将这个数的二倍返回
@Test
public void testFindNum()
{
List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(values.stream().filter(e -> e > 3).filter(e -> e % 2 == 0).map(e -> e * 2).findFirst().get());
}
// 从一个数字集合中找到第一个大于3的数,并将这个数的二倍返回,改写01
@Test
public void testFindNum01()
{
List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(values.stream().filter(Java8StreanAPI::isGreaterThan3).filter(Java8StreanAPI::isEven).map(Java8StreanAPI::doubleIt).findFirst().get());
}
public static boolean isGreaterThan3(int number)
{
return number > 3;
}
public static boolean isEven(int number)
{
return number % 2 == 0;
}
public static int doubleIt(int number)
{
return number * 2;
}
// 从一个数字集合中找到第一个大于3的数,并将这个数的二倍返回,改写02
@Test
public void testFindNum02()
{
List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(values.stream().filter(isGreaterThan301()).filter(isEven01()).map(doubleIt01()).findFirst().get());
}
@Test
public void testFindNum03()
{
List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(values.stream().filter(isGreaterThan301()).filter(isEven01()).map(doubleIt01()).findFirst().get());
}
@Test
public void testFindNum04()
{
List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(values.stream().filter(isGreaterThan().apply(3)).filter(isEven01()).map(doubleIt01()).findFirst().get());
}
@Test
public void testFindNum05()
{
System.out.println(IntStream.range(1, 10).filter(number -> number > 3).filter(number -> number % 2 == 0).map(number -> number * 2).findFirst().getAsInt());
}
public static Predicate isGreaterThan301()
{
return number -> number > 3;
}
public static Predicate isEven01()
{
return number -> number % 2 == 0;
}
// 函数式接口之间可以转换的[虽然这是个]Function函数式接口,但是返回值也可以是其他的函数式接口,在调用的时候就不同了参考testFindNum04方法
public static Function> isGreaterThan()
{
// 解释: pivot -> (number -> number > pivot)
// 括号里面的看出是一坨,整个Lamdab有入有出是Function接口,括号里面也一个Lamdab,是一个Predicate接口,有入有出,只是出规定必须是Boolean类型,这个出同样作为Function接口的出,Function的出可以是任意类型
return pivot -> number -> number > pivot; // pivot表示传进来的元素,number则是Stream里面的元素,这种的话可读性就差了,不好理解了
}
public static Function doubleIt01()
{
return number -> number * 2;
}
@Test
public void testSum()
{
List values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(values.parallelStream().mapToInt(element -> element * 2).sum());
System.out.println(values.parallelStream().mapToInt(element -> element * 2).reduce(0, (a, b) -> a + b));
}
// 原始版[线程控制没管,这里只看Lamdab和StreamAPI]
@Test
public void testMutilThread()
{
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++ )
{
int index = i;
executorService.submit(new Runnable()
{
@Override
public void run()
{
System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + index);
}
});
}
System.out.println("All Task was started....");
executorService.shutdown();
}
@Test
public void testMutilThread01()
{
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++ )
{
int index = i;
executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + index));
}
System.out.println("All Task was started....");
executorService.shutdown();
}
@Test
public void testMutilThread02()
{
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 利用Stream可以产生集合
IntStream.range(0, 10).forEach(i -> {
int index = i;
executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + index));
});
System.out.println("All Task was started....");
executorService.shutdown();
}
// 使用Stream构建For(int i=0; i values = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntStream.range(0, 10).forEach(i -> {
int index = i;
System.out.println("index is >>>> " + index); // 结果:0~9, IntStream.range(0, 10)左闭右开区间即[0, 10)
});
IntStream.range(0, values.size()).forEach(i -> {
int index = i;
System.out.println("index is >>>> " + index);
});
int[] array = IntStream.range(0, 10).toArray();
for (int j : array)
{
System.out.println("index is >>>> " + j);
}
}
@Test
public void testMutilThread03()
{
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 利用Stream可以产生集合
IntStream.range(0, 10).forEach(i -> executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index is:" + i)));
// 利用Google Guava产生Range也可以产生集合
ContiguousSet.create(Range. closed(0, 10), DiscreteDomain.integers()).stream().forEach(
i -> executorService.submit(() -> System.out.println(Thread.currentThread().getName() + ">>>>exe Task--index2 is:" + i)));;
System.out.println("All Task was started....");
executorService.shutdown();
}
// 原始
@Test
public void testGet100PrimeSqrt()
{
List sqrtOfFrist100Primes = new ArrayList<>();
int index = 1;
while (sqrtOfFrist100Primes.size() < 100)
{
if (this.isPrime(index))
{
sqrtOfFrist100Primes.add(Math.sqrt(index));
}
index++ ;
}
System.out.println(String.format("Computer %d values, first is %g, last is %g", sqrtOfFrist100Primes.size(), sqrtOfFrist100Primes.get(0),
sqrtOfFrist100Primes.get(sqrtOfFrist100Primes.size() - 1)));
}
@Test
public void testGet100PrimeSqrt01()
{
// 注意这里不能使用e + 1不能写成e++,否则JVM停不下来
List sqrtOfFrist100Primes = Stream.iterate(1, e -> e + 1).filter(Java8StreanAPI::isPrime01).map(Math::sqrt).limit(100).collect(
Collectors.toCollection(ArrayList::new));// ArrayList::new
// 相当与调用new这个构造方法,等价Collectors.toList());
System.out.println(String.format("Computer %d values, first is %g, last is %g", sqrtOfFrist100Primes.size(), sqrtOfFrist100Primes.get(0),
sqrtOfFrist100Primes.get(sqrtOfFrist100Primes.size() - 1)));
}
@Test
public void testListDirFileName()
{
File dir = new File("D:\\SoftTools");
File[] children = dir.listFiles();
if (Objects.nonNull(children))
{
// 数组不能直接调用stream方法,要转化一下,Collectors作为工具类,可以静态导入
System.out.println(Stream.of(children).map(File::getName).map(String::toUpperCase).collect(Collectors.joining(", ")));
}
}
// =====================================追加==========================================
// Spliterator:Stream流的迭代器的使用,并行流处理优势强大
@Test
public void testSpliterator01()
{
List persons01 = new ArrayList<>();
persons01.add(new Person(1, "name" + 1, 10));
persons01.add(new Person(2, "name" + 2, 21));
persons01.add(new Person(3, "name" + 3, 34));
persons01.add(new Person(4, "name" + 4, 6));
persons01.add(new Person(5, "name" + 5, 55));
// 获取一个元素
Spliterator spliterator = persons01.spliterator();
spliterator.tryAdvance(System.out::println);
System.out.println("----------------");
// while(persons01.spliterator().tryAdvance(System.out::println));错误写法,死循环
while (spliterator.tryAdvance(System.out::println));
System.out.println("----------------");
// 从输出可以看出同一个spliterator只能使用一次
spliterator.forEachRemaining(System.out::println);
}
@Test
public void testSpliterator02()
{
List persons01 = new ArrayList<>();
persons01.add(new Person(1, "name" + 1, 10));
persons01.add(new Person(2, "name" + 2, 21));
persons01.add(new Person(3, "name" + 3, 34));
persons01.add(new Person(4, "name" + 4, 6));
persons01.add(new Person(5, "name" + 5, 55));
// 获取一个元素
Spliterator spliterator = persons01.spliterator();
spliterator.tryAdvance(System.out::println);
System.out.println("----------------");
while (spliterator.tryAdvance(System.out::println));
System.out.println("----------------");
Spliterator spliterator02 = persons01.spliterator();
spliterator02.forEachRemaining(System.out::println);
}
@Test
public void testSpliterator03()
{
List persons01 = new ArrayList<>();
persons01.add(new Person(1, "name" + 1, 10));
persons01.add(new Person(2, "name" + 2, 21));
persons01.add(new Person(3, "name" + 3, 34));
persons01.add(new Person(4, "name" + 4, 6));
persons01.add(new Person(5, "name" + 5, 55));
persons01.add(new Person(6, "name" + 6, 431));
Spliterator spliterator = persons01.spliterator();
// 将Spliterator拆分成两个Spliterator,这两个Spliterator包含所有的元素,如果无法拆分返回null
Spliterator trySplit = spliterator.trySplit();
Optional.ofNullable(trySplit).ifPresent(x -> {
x.forEachRemaining(System.out::println);
});
System.out.println("----------------");
spliterator.forEachRemaining(System.out::println);
}
@Test
public void testBi()
{
System.getProperties().entrySet().forEach(System.out::println);
// BiConsumer,两个入参,无返回,带有Bi开头的函数式接口都是两个参数的,如BiFunction、BiPredicate
System.getProperties().forEach((x, y) -> {
System.out.println(x + " ----> " + y);
});
System.out.println(System.getProperty("os.name"));
}
// Map的Stream示例,主要处理过滤条件Map对象,过滤掉了null和空字符串 等操作
public Map parseMapForFilter(Map map)
{
if (map == null)
{
return null;
}
else
{
map = map.entrySet().stream().filter((e) -> checkValue(e.getValue())).collect(Collectors.toMap((e) -> (String)e.getKey(), (e) -> e.getValue()));
}
return map;
}
private static boolean checkValue(Object object)
{
if (object instanceof String && "".equals(object))
{
return false;
}
if (null == object)
{
return false;
}
return true;
}
// 结合Optional优化,吊
public Map parseMapForFilterByOptional(Map map)
{
return Optional.ofNullable(map).map((v) -> {
Map params = v.entrySet().stream().filter((e) -> checkValue(e.getValue())).collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));
return params;
}).orElse(null);
}
@Test
public void testMap()
{
Map map = new HashMap<>();
map.put("a", "");
map.put("b", null);
map.put("c", "c");
this.parseMapForFilter(map).entrySet().stream().forEach(x -> {
System.out.println(x.getKey() + "---" + x.getValue());
});
this.parseMapForFilterByOptional(map).entrySet().stream().forEach(x -> {
System.out.println(x.getKey() + "---" + x.getValue());
});
}
// ===============================Collectors收集器工具API==================================
// toMap/groupingBy/partitioningBy这几个会收集为Map
@Test
public void getMapValues()
{
Map> map = new HashMap<>();
Stack stack = new Stack<>();
for (int i = 0; i < 10; i++ )
{
stack.push("str" + i);
}
map.put("a", stack);
List collect = map.entrySet().stream().map(Map.Entry::getValue).flatMap(Stack::stream).collect(Collectors.toList());
collect.forEach(System.out::println);
}
@Test
public void testList2Map()
{
Map> map = new HashMap<>();
Stack stack = new Stack<>();
for (int i = 0; i < 10; i++ )
{
stack.push("str");
}
map.put("a", stack);
map.put("b", stack);
Map map2 = map.entrySet().stream().collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.counting()));
List persons01 = new ArrayList<>();
persons01.add(new Person(1, "name" + 1, 10));
persons01.add(new Person(2, "name" + 2, 21));
persons01.add(new Person(3, "name" + 3, 34));
persons01.add(new Person(4, "name" + 4, 6));
persons01.add(new Person(5, "name" + 5, 55));
persons01.add(new Person(6, "name" + 6, 431));
// 这行代码会打印name,List收集为Map
Map collect = persons01.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
map2.entrySet().forEach(System.out::println);
collect.entrySet().forEach(System.out::println);
}
List students = new ArrayList<>();
List persons = new ArrayList<>();
@Before
public void CreateData()
{
persons.add(new Person(1, "name" + 1, 10));
persons.add(new Person(2, "name" + 2, 21));
persons.add(new Person(3, "name" + 3, 34));
persons.add(new Person(4, "name" + 4, 6));
persons.add(new Person(5, "name" + 5, 55));
persons.add(new Person(6, "name" + 6, 431));
students.add(new Student("ZS1", 20.2, "SC", 45.3, 34, new Date(), "M"));
students.add(new Student("ZS2", 89.2, "SC", 34.3, 34, new Date(), "M"));
students.add(new Student("LS", 23.1, "SC", 69.5, 56, new Date(), "W"));
students.add(new Student("WW1", 14.4, "BJ", 75.5, 632, new Date(), "W"));
students.add(new Student("WW2", 78.4, "BJ", 75.0, 632, new Date(), "W"));
students.add(new Student("WW", 34.4, "BJ", 89.0, 632, new Date(), "M"));
students.add(new Student("ZL1", 56.8, "SH", 99.8, 86, new Date(), "M"));
students.add(new Student("ZL", 99.8, "SH", 45.8, 86, new Date(), "W"));
students.add(new Student("ZL3", 99.8, "SH", 99.8, 86, new Date(), "M"));
}
/*
* averagingDouble(ToDoubleFunction super T> mapper) averagingDouble/Int/Long 汇总求出平均值
*/
@Test
public void averagingDoubleTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Double collect = students.stream()
// 静态导入Collectors, 适合于静态方法多次使用的情况下,如果只使用一两次没必要,如果使用里面的静态方法很多,可以使用.*导入全部的静态方法
.collect(averagingDouble(student -> student.getHeight()));
System.out.println(collect);
}
/*
* collectingAndThen(Collector downstream, Function finisher) collectingAndThen 将收集来的结果进行另外的转换
* 本例中将收集的结果放入不可变List中。
*/
@Test
public void collectingAndThenTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
List collect = students.stream().map(student -> student.getName()).collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
Stream collect2 = students.stream().map(student -> student.getName())
// 收集为一个List后,然后对这个List执行一个Function的操作,x就是收集后的List
.collect(Collectors.collectingAndThen(Collectors.toList(), x -> {
return x.stream().map(String::toLowerCase);
}));
collect.forEach(System.out::println);
collect2.forEach(System.out::println);
}
/*
* 简单的计数器,简单的计算流元素。
*/
@Test
public void countingTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
// 元素的个数
Long collect = students.stream().collect(Collectors.counting());
System.out.println(collect);
}
/*
* groupingBy(Function super T,? extends K> classifier) 通过省份将学生分组到List中。
*/
@Test
public void groupingByToList()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
// by的属性就是key,返回一个这个key的map
Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom));
collect.entrySet().forEach(System.out::println);
}
/*
* groupingBy(Function super T,? extends K> classifier, Collector super T,A,D> downstream) 通过省份将学生分组到Set中。
*/
@Test
public void groupingByToSet()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, Collectors.toSet()));
collect.entrySet().forEach(System.out::println);
}
/*
* groupingBy(Function super T,? extends K> classifier, Supplier mapFactory, Collector super T,A,D> downstream)
* 通过省份将学生分组到Set中。并返回一个TreeMap[按key会排序],TreeMap和TreeSet都会排序.
*/
@Test
public void groupingByToTreeMap()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
TreeMap> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom, TreeMap::new, Collectors.toSet()));
collect.entrySet().forEach(System.out::println);
}
/*
* groupingByConcurrent(Function super T,? extends K> classifier) 返回并发的Map,即ConcurrentMap
*/
@Test
public void groupingByConcurrentTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
ConcurrentMap> collect = students.stream().collect(Collectors.groupingByConcurrent(Student::getFrom));
collect.entrySet().forEach(System.out::println);
}
/*
* groupingByConcurrent(Function super T,? extends K> classifier, Collector super T,A,D> downstream)
* 返回ConcurrentMap,类似上面的Map
*/
@Test
public void groupingByConcurrentToSet()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
ConcurrentMap> collect = students.stream().collect(Collectors.groupingByConcurrent(Student::getFrom, Collectors.toSet()));
collect.entrySet().forEach(System.out::println);
}
/*
* 返回并发的Map,排序的,他实现了SortedMap
*/
@Test
public void groupingByConcurrentToSkipMap()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
ConcurrentSkipListMap> collect = students.stream().collect(
Collectors.groupingByConcurrent(Student::getFrom, ConcurrentSkipListMap::new, Collectors.toSet()));
collect.entrySet().forEach(System.out::println);
}
/*
* 返回所有的学生的姓名 joining() joining(CharSequence delimiter) joining(CharSequence delimiter, CharSequence prefix,
* CharSequence suffix)
*/
@Test
public void joiningTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
String collect = students.stream().map(Student::getName).collect(Collectors.joining());
String collect2 = students.stream().map(Student::getName).collect(Collectors.joining(","));
String collect3 = students.stream().map(Student::getName).collect(Collectors.joining(",", "{", "}"));
// 先执行collect操作后再执行第二个参数的表达式。这里是先拼接字符串,再在最后+ "d"。
Collectors.collectingAndThen(Collectors.joining(","), x -> x + "d");
System.out.println(collect);
System.out.println(collect2);
System.out.println(collect3);
}
/*
* mapping(Function super T,? extends U> mapper, Collector super U,A,R> downstream) 以省份分组,并返回学生姓名的集合。
*/
@Test
public void mappingTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom,
// mapping对执行一个Function动作后的结果进行收集
Collectors.mapping(Student::getName, Collectors.toList())));
// 跟map操作类似,只是参数有点区别。map后,执行一个Function操作,在收集
System.out.println(Stream.of("a", "b", "c").collect(Collectors.mapping(x -> x.toUpperCase(), Collectors.joining(","))));
collect.entrySet().forEach(System.out::println);
}
/**
* maxBy(Comparator super T> comparator)
*
* 返回身高最高的学生
*/
@Test
public void maxByTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Student student = students.stream().collect(Collectors.maxBy(Comparator.comparingDouble(Student::getHeight))).get();
System.out.println(student);
}
/**
* @return 返回体重最轻的学生。
*/
@Test
public void minByTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Student student = students.stream().collect(Collectors.minBy(Comparator.comparingDouble(Student::getWeight))).get();
System.out.println(student);
}
/**
* partitioningBy(Predicate super T> predicate)
*
* 根据Predicate将流分割,并将它们组织成一个 Map>返回。
*/
@Test
public void partitioningByTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map> collect = students.stream().collect(
// 给定一个Predicate来分区
Collectors.partitioningBy(student -> "SC".equals(student.getFrom())));
collect.entrySet().forEach(System.out::println);
// 输出结果如下
// false=[Student [name=WW, height=14.4, from=BJ, weight=75.0, scores=632.0,
// birthday=Tue May 28 10:24:58 SGT 2019], Student [name=ZL, height=56.8,
// from=SH, weight=99.8, scores=86.0, birthday=Tue May 28 10:24:58 SGT 2019]]
// true=[Student [name=ZS, height=20.2, from=SC, weight=45.3, scores=34.0,
// birthday=Tue May 28 10:24:58 SGT 2019], Student [name=LS, height=23.1,
// from=SC, weight=69.5, scores=56.0, birthday=Tue May 28 10:24:58 SGT 2019]]
}
/**
* partitioningBy(Predicate super T> predicate, Collector super T,A,D>
* downstream)
*
* 根据Predicate将流分割,并将它们组织成一个 Map>返回。
*/
@Test
public void partitioningByToSet()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map> collect = students.stream().collect(Collectors.partitioningBy(student -> "SH".equals(student.getFrom()), Collectors.toSet()));
collect.entrySet().forEach(System.out::println);
}
/**
* reducing(BinaryOperator op)
*
* BinaryOperator中有两个静态方法,是用于比较两个数字或字符串的大小
*
* 返回一个班级中每个省份的身高最高的学生。
*/
@SuppressWarnings("unused")
@Test
public void reducingTest1()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Comparator byHeight2 = (s1, s2) -> Double.compare(s1.getHeight(), s2.getHeight());
Comparator byHeight3 = (s1, s2) -> Double.valueOf(s1.getHeight()).compareTo(Double.valueOf(s2.getHeight()));
Comparator byHeight = Comparator.comparing(Student::getHeight);
Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom,
// 收集身高最高的,BinaryOperator是一个BiFunction,有一个问题就是最高的有两个的话,结果中只有一个
Collectors.reducing(BinaryOperator.maxBy(byHeight))));
collect.entrySet().forEach(System.out::println);
}
/**
* reducing(BinaryOperator op)
*
* 返回一个班级中每个省份的身高最高的学生。
*/
@Test
public void reducingTest2()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom,
// 收集身高最高的,BinaryOperator是一个BiFunction, -> 如果使用Lamdab的自动返回后面不能有{},
// Function式是有入有出的, BinaryOperator是BiFunction,则是两个入一个出, 这里s1.getHeight()
// 返回的是基本类型double,不是对象类型,没有方法了,所以不能在.继续操作
// 需要包装成Double才能继续.操作Double提供的方法,或者使用compareTo方法,这里强转有精度损失,不管,这里只是示例
Collectors.reducing(BinaryOperator.maxBy((s1, s2) -> (int)(s1.getHeight() - s2.getHeight())))));
collect.entrySet().forEach(System.out::println);
// reducing的另一个重载
// 在求累计值的时候,还可以对参数值进行改变,这里是都+1后再求和。跟reduce方法有点类似,但reduce方法没有第二个参数。
System.out.println(Stream.of(1, 3, 4).collect(Collectors.reducing(0, x -> x + 1, (x, y) -> x + y)));
}
/**
* reducing(BinaryOperator op)
*
* 返回一个班级中每个省份的身高最高的学生。
*/
@Test
public void reducingTest3()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map> collect = students.stream().collect(Collectors.groupingBy(Student::getFrom,
// 收集身高最高的,BinaryOperator是一个BiFunction, -> 如果使用Lamdab的自动返回后面不能有{},
// Function式是有入有出的, BinaryOperator是BiFunction,则是两个入一个出
Collectors.reducing(BinaryOperator.maxBy((s1, s2) -> Double.compare(s1.getHeight(), s2.getHeight())))));
collect.entrySet().forEach(System.out::println);
}
/**
* summarizingDouble(ToDoubleFunction super T> mapper)
*
* summarizingDouble/Int/Long 用另一种方式来求学生的平均体重。
*/
@Test
public void summarizingDoubleTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
double average = students.stream().collect(Collectors.summarizingDouble(Student::getWeight)).getAverage();
System.out.println(average);
}
/**
* 求和,如果元素不存在,则结果为0.
*
* 求学生的平均身高
*/
@Test
public void summingDoubleTest()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
// 这样如果没添加返回值接受,就会报错,即没有Double collect = 后面的
Double collect = students.stream().collect(Collectors.summingDouble(Student::getHeight)).doubleValue() / students.size();
Double collect1 = students.stream().collect(Collectors.summingDouble(Student::getHeight));
Double double1 = collect1 / students.size();
System.out.println(collect + "---" + double1);
}
/**
* 将元素放入制定的集合中
*/
@Test
public void toCollectionToList()
{
students.stream().map(Student::getName).collect(Collectors.toCollection(ArrayList::new)).forEach(System.out::println);
}
/**
* 获取学生的成绩
*/
@Test
public void toConcurrentMapTest()
{
students.stream().collect(Collectors.toConcurrentMap(Student::getName, Student::getScores)).entrySet().forEach(System.out::println);
}
@Test
public void toMapTest()
{
Map collect = students.stream().collect(Collectors.toMap(Student::getName, Student::getBirthday));
collect.entrySet().forEach(System.out::println);
}
// 收集成实体本身map
@Test
public void getIdAccountMap()
{
Map collect = students.stream()
// student -> student是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅
.collect(Collectors.toMap(Student::getName, student -> student));
collect.entrySet().forEach(System.out::println);
}
// 收集成实体本身map,与上面等价
@Test
public void getIdAccountMap1()
{
Map collect = students.stream()
// student -> student是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅
.collect(Collectors.toMap(Student::getName,
// Returns a function that always returns its input argument
Function.identity()));
collect.entrySet().forEach(System.out::println);
}
// 上面这个方法可能报错(java.lang.IllegalStateException: Duplicate
// key),因为name是有可能重复的。toMap有个重载方法,可以传入一个合并的函数来解决key冲突问题:
@Test
public void getIdAccountMap2()
{
Map collect = students.stream()
// student -> student是一个返回本身的lambda表达式,其实还可以使用Function接口中的一个默认方法代替,使整个方法更简洁优雅
.collect(Collectors.toMap(Student::getName, Function.identity(), (k1, k2) -> k2));
collect.entrySet().forEach(System.out::println);
}
// 指定具体收集的map
// toMap还有另一个重载方法,可以指定一个Map的具体实现,来收集数据:
@Test
public void getNameAccountMap()
{
LinkedHashMap collect = students.stream().collect(Collectors.toMap(Student::getName, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new));
collect.entrySet().forEach(System.out::println);
}
/**
*
* @param address
* @return
* @throws FileNotFoundException
* 统计一篇文章的词频直方图。
*/
public Map toMapForWord(String address)
throws FileNotFoundException
{
Objects.requireNonNull(address, "The parameter cannot be empty");
Pattern whitespace = Pattern.compile("\\s+");
try (BufferedReader reader = new BufferedReader(new FileReader(new File(address))))
{
return reader.lines().flatMap(string -> whitespace.splitAsStream(string)).collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
}
catch (Exception e)
{
// Error log
return new HashMap();
}
}
/**
* 返回每个省份的最高学生的所有信息。
*/
@Test
public void accountMaxHeightByProvince()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map>> collect = students.stream().collect(
Collectors.groupingBy(Student::getFrom, Collectors.groupingBy(Student::getName, Collectors.maxBy(Comparator.comparingDouble(Student::getHeight)))));
collect.entrySet().forEach(System.out::println);
}
/**
* 先以性别分组然后再以省份分组,返回学生的平均身高。
*/
@Test
public void averageHeightByProvince()
{
Objects.requireNonNull(students, "The parameter cannot be empty");
Map> collect = students.stream().collect(
Collectors.partitioningBy(student -> "M".equals(student.getGender()), Collectors.groupingBy(Student::getFrom, Collectors.averagingDouble(Student::getHeight))));
collect.entrySet().forEach(System.out::println);
}
}
// 辅助类
class Transaction
{
public static final String GROCERY = "JAVA8";
private Integer id;
private String type;
private String value;
public Transaction()
{
super();
}
public Transaction(Integer id, String type, String value)
{
super();
this.id = id;
this.type = type;
this.value = value;
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
@Override
public String toString()
{
return "Transaction [id=" + id + ", type=" + type + ", value=" + value + "]";
}
}
class Student
{
private String name;
private Double height;
private String from;
private Double weight;
private Double scores;
private Date birthday;
private String gender;
public Student(String name, double height, String from, double weight, double scores, Date birthday, String gender)
{
super();
this.name = name;
this.height = height;
this.from = from;
this.weight = weight;
this.scores = scores;
this.birthday = birthday;
this.gender = gender;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public double getHeight()
{
return height;
}
public void setHeight(double height)
{
this.height = height;
}
public String getFrom()
{
return from;
}
public void setFrom(String from)
{
this.from = from;
}
public double getWeight()
{
return weight;
}
public void setWeight(double weight)
{
this.weight = weight;
}
public double getScores()
{
return scores;
}
public void setScores(double scores)
{
this.scores = scores;
}
public Date getBirthday()
{
return birthday;
}
public void setBirthday(Date birthday)
{
this.birthday = birthday;
}
public String getGender()
{
return gender;
}
public void setGender(String gender)
{
this.gender = gender;
}
@Override
public String toString()
{
return "Student [name=" + name + ", height=" + height + ", from=" + from + ", weight=" + weight + ", scores=" + scores + ", birthday=" + birthday + ", gender=" + gender
+ "]";
}
}