流(Stream)的基本介绍
Stream的操作步骤
- 创建Stream
一个数据源(如:集合、数组),获取一个流 - 中间操作
一个中间操作链,对数据源的数据进行处理 - 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
创建Stream
public class Demo01 {
List<Person> people = Arrays.asList(
new Person("A", 24),
new Person("B", 44),
new Person("E", 53),
new Person("C", 53),
new Person("D", 19));
@Test
public void test01() {
// 1. 通过Collection集合提供的stream()
ArrayList<Object> list = new ArrayList<>();
Stream<Object> stream = list.stream();
// 2.通过Arrays中静态方法获取数组流
int arr[] = {1, 2, 3};
IntStream stream1 = Arrays.stream(arr);
// 3.通过Stream中的静态方法
Stream<String> stream2 = Stream.of("aa", "bb", "cc");
// 4.创建无限流
// 迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
stream3.limit(10);
stream3.forEach(System.out::println);
// 生成
Stream.generate(() -> Math.random()).limit(5).forEach(System.out::println);
}
}
中间操作
1.筛选与切片
@Test
public void test02() {
// 内部迭代
// 中间操作:不会执行任何操作
Stream<Person> stream = people.stream().
filter((e) -> e.age > 30);
// 终止操作:一次性执行全部内容
stream.forEach(System.out::println);
}
@Test
public void test03() {
people.stream()
.filter((e) -> e.age > 20)
// 取前两个
.limit(2)
.forEach(System.out::println);
}
@Test
public void test04() {
people.stream()
.filter((e) -> e.age > 20)
// 跳过前两个
.skip(2)
.forEach(System.out::println);
}
@Test
public void test05() {
people.stream()
.filter((e) -> e.age > 20)
.skip(2)
// 去重
.distinct()
.forEach(System.out::println);
}
2.映射
@Test
public void test06() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
System.out.println("提取Person名字");
people.stream().map(Person::getName)
.forEach(System.out::println);
}
3.排序
@Test
public void test07() {
// 排序
List<String> list = Arrays.asList("ddd", "aaa", "bbb", "ccc");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("--------------------");
// 年龄一样按照姓名排
people.stream()
.sorted((e1, e2) -> {
if (e1.age == e2.age) {
return e1.name.compareTo(e2.name);
} else {
return e1.age - e2.age;
}
})
.forEach(System.out::println);
}
终止操作
终止操作会从流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。
1.查找与匹配
@Test
public void test08() {
// 查找与匹配
boolean b = people.stream()
// 是否匹配所有元素
.allMatch((e) -> e.name.equals("B"));
System.out.println(b);
System.out.println("----------------");
// 至少有一个匹配
boolean b1 = people.stream().anyMatch((e) -> e.name.equals("B"));
System.out.println(b1);
System.out.println("----------------");
// 是否没有匹配所有元素
boolean b2 = people.stream().noneMatch((e) -> e.name.equals("B"));
System.out.println(b2);
System.out.println("-------------------------");
// 找出第一个,年龄最小的,最终结果有可能为空时
Optional<Person> first = people.stream().sorted(Comparator.comparing(e -> e.age))
.findFirst();
System.out.println(first.get());
System.out.println("-------------------------");
Optional<Person> optional = people.stream()
.filter((e) -> e.age > 30)
.findAny();
System.out.println(optional.get());
}
@Test
public void test09() {
long count = people.stream().count();
System.out.println(count);
// 获取年龄最大
Optional<Person> max = people.stream().max(Comparator.comparingInt(e -> e.age));
System.out.println(max.get());
System.out.println("----------------------");
// 获取最小名字
Optional<String> min = people.stream()
.map(Person::getName)
.min(String::compareTo);
System.out.println(min.get());
}
2.归约
@Test
public void test10() {
// 归约
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer reduce = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(reduce);
System.out.println("---------------");
// 年龄总和
Optional<Integer> op = people.stream()
.map(Person::getAge)
.reduce(Integer::sum);
System.out.println("年龄总和:" + op.get());
}
3.收集
Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
@Test
public void test11() {
// 收集
// 提取所有姓名
List<String> list = people.stream()
.map(Person::getName)
.collect(Collectors.toList());
System.out.println(list);
System.out.println("----------------");
HashSet<String> set = people.stream()
.map(Person::getName)
.collect(Collectors.toCollection(HashSet::new));
set.forEach(System.out::println);
}
@Test
public void test12() {
// 收集
Long set = people.stream()
.collect(Collectors.counting());
System.out.println(set);
// 年龄平均值
Double age = people.stream()
.collect(Collectors.averagingInt(Person::getAge));
System.out.println(age);
System.out.println("-------------------------");
IntSummaryStatistics agesum = people.stream()
.collect(Collectors.summarizingInt(Person::getAge));
System.out.println(agesum);
System.out.println("-------------------------");
// 获取年龄字符最大
Optional<Person> name = people.stream().max(Comparator.comparing(e -> e.name));
System.out.println(name.get());
}
4.分组、分区
@Test
public void test13() {
// 分组
// 按照相同年龄分组
Map<Integer, List<Person>> map = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
map.entrySet().forEach(System.out::println);
}
@Test
public void test14() {
// 多级分组
Map<Integer, Map<String, List<Person>>> map = people.stream()
.collect(Collectors.groupingBy(Person::getAge, Collectors.groupingBy((e) -> {
if (e.getAge() <= 35) {
return "青年";
} else {
return "中年";
}
})));
map.entrySet().forEach(System.out::println);
}
@Test
public void test15() {
// 分区
// 满足条件一个区
Map<Boolean, List<Person>> map = people.stream()
.collect(Collectors.partitioningBy((e) -> e.age > 35));
map.entrySet().forEach(System.out::println);
}
@Test
public void test16() {
// 分区
// 满足条件一个区
IntSummaryStatistics statistics = people.stream()
.collect(Collectors.summarizingInt(Person::getAge));
System.out.println(statistics.getAverage());
System.out.println(statistics.getCount());
System.out.println(statistics.getMax());
}
@Test
public void test17() {
// 分区
// 满足条件一个区
String s = people.stream()
.map(Person::getName)
// 中间 首部 尾部
.collect(Collectors.joining(",", "<", ">"));
System.out.println(s);
}
并行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Fork/Join 框架介绍
Fork/Join 框架:就是在必要的情况下,将一个大任务,进形拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运行的结果进行join汇总。
Fork/Join 框架与传统线程池的区别:
采用“工作窃取”模式(work-stealing):
当执行新的任务时,它可以将其拆分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能。
public class ForkJoinDemo extends RecursiveTask<Long> {
private static final long serialVersionUID = 13465648909L;
private long start;
private long end;
private static final long THRESHOLD = 10000;
public ForkJoinDemo(long start, long end) {
this.start = start;
this.end = end;
}
public static void main(String args[]) {
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0, 10000000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println(Duration.between(start, end).toMillis());
}
protected Long compute() {
long length = end - start;
if (length <= THRESHOLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
long mid = (start + end) / 2;
ForkJoinDemo left = new ForkJoinDemo(start, mid);
left.fork();
ForkJoinDemo right = new ForkJoinDemo(mid + 1, end);
right.fork();
return left.join() + right.join();
}
}
}
Java8并行流
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
@Test
public void test20() {
// 并行流
long l = LongStream.rangeClosed(0, 10000000L)
.parallel()
.reduce(0, Long::sum);
System.out.println(l);
}
Stream练习
@Test
public void test18() {
// 给定数字列表
// 返回每个数字列表的平方列表
Integer[] integers = new Integer[]{1, 2, 3, 4, 5};
Arrays.stream(integers)
.map((x) -> x * x)
.forEach((i) -> System.out.print(i + " "));
}
@Test
public void test19() {
// 获取people集合中数目
Optional<Integer> optional = people.stream()
.map((e) -> 1)
.reduce(Integer::sum);
System.out.println(optional.get());
}