/*
了解Steam
Java8中有两大最为重要的改变:Lambda表达式、StreamAPI(steam流,java.util.stream.*)
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、
过滤和映射数据等操作。
使用StreamAPI对集合数据进行操作,就类似于使用SQL执行的数据库查询。
也可以使用StreamAPI来并行执行操作。简而言之,StreamAPI提供了一种高效且易于使用的处理数据的方式。
流(Stream)到底的什么呢?
流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算”。
注意:
1、Stream自己不会存储元素。
2、Stream不会改变源对象。相反,它们会返回一个持有结果的新Stream。
3、Stream操作是延迟执行的。这意味着它们会等到需要结果的时候才执行。
Stream的操作三个步骤:
1、创建Stream
一个数据源(如集合、数组),获取一个流
2、中间操作
一个中间操作链,对数据源的数据进行处理
3、终止操作
一个终止操作,执行中间操作,并产生结果
创建Stream:
Java8中的Collection接口被扩展,提供两个获取流的方法:list.stream();
|- default Stream<E> stream() 返回一个顺序流
|- default Stream<E> parallelStream() 返回一个并行流
由数组创建流:
Java8中的Arrays的静态方法stream()以获取数组流:
|- static <T> Stream<T> stream(T[] array) 返回一个流
|- public static IntStream stream(int[] array)
|- public static LongStream stream(long[] array)
|- public static DoubleStream stream(double[] array)
由值创建流:
可以使用静态方法Stream.of(),通过显示值创建一个流。它可以接收任意数量的参数。
|- public static<T> Stream<T> of(T... values) 返回一个流
由函数创建流:创建无限流
可以使用静态方法Stream.iterate()和Stream.generate(),创建无限流。
|- 迭代
public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f)
|- 生成
public static<T> Stream<T> generate(Supplier<T> s)
Stream的中间操作:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。
而在终止操作时一次性全部处理,称为“惰性求值”。
Optional类是一个可以为null的容器对象,如果值存在则isPresent()方法返回true,调用get()返回该对象。
*/
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamAPI {
public static List<Product> getProduct(){
List<Product> products = Arrays.asList(
new Product("iphone","电子产品",7000,3),
new Product("book1","书籍",20,10),
new Product("computer","电子产品",10000,5),
new Product("vivo","电子产品",4000,4),
new Product("book2","书籍",15,20),
new Product("chicken","食品",25,2),
new Product("rice","食品",60,3),
new Product("lol","游戏",80,1),
new Product("cf","游戏",50,5),
new Product("picture","纪念品",12,6),
new Product("flower","纪念品",20,1),
new Product("nick","服装",199,3)
);
return products;
}
/*
创建stream流
*/
public static void createStream(){
// 通过java.util.Collection.stream()方法用集合创建流
List<String> strList = Arrays.asList("H","e","l","l","o");
// 创建一个顺序流
Stream<String> strStream = strList.stream();
// 创建一个并行流
Stream<String> parallelStream = strList.parallelStream();
/*
stream和parallelStream区分:
stream是顺序流,由主线程按顺序对流执行操作。
parallelStream流是并行流,内部以多线程并行执行的方式对流进行操作,
前提是流中的数据处理没有顺序要求。
如果流中数据量足够大,并行流可以加快处理速度。
除了可以创建并行流,也可以调用顺序流的parallel()方法,把流转为并行流。
*/
// 寻找出strList里第一个长度大于6的字符串,使用并行流
Optional<String> findFirst = strList.stream().parallel().filter(x->x.length()>6).findFirst();
// 使用java.util.Arrays.stream(T[] array)方法用数组创建流
int[] array = new int[]{1,3,5,6,8};
IntStream intStream = Arrays.stream(array);
// 使用Stream的静态方法:of()、iterate()、generate()方法
Stream<Integer> integerStream1 = Stream.of(1,3,2,4,6);
integerStream1.forEach(System.out::print); // 13246
System.out.println();
Stream<Integer> integerStream2 = Stream.iterate(0,(x)->x+3).limit(4);
integerStream2.forEach(System.out::print); // 0369
System.out.println();
Stream<Double> integerStream3 = Stream.generate(Math::random).limit(3);
integerStream3.forEach(System.out::print); // 0.218 0.932 0.887
}
/*
遍历/匹配(foreach/find/match)
Stream流也是支持类似集合的遍历和匹配元素的,只是Stream流中的元素是以Optional类型存在的。
所有Stream流的遍历、匹配非常简单。
*/
public static void ForeachAndFind(){
List<Product> list = getProduct();
// 遍历输出符合条件的product 类别为”电子产品“
list.stream().filter(product -> product.getCategory().equals("电子产品")).forEach(System.out::println);
// 匹配第一个类别为”电子产品“且价格大于8000的商品
Optional<Product> findFirst = list.stream().filter(
product -> product.getCategory().equals("电子产品") && product.getPrice()>8000
).findFirst();
System.out.println("第一个类别为”电子产品“且价格大于5000的商品:"+findFirst.get()); // computer
// 任意匹配,返回流中任意元素。返回任意一本书
Optional<Product> findAny = list.stream().parallel().filter(product -> product.getCategory().equals("书籍")).findAny();
System.out.println("findAny = " +findAny.get()); // book1/book2
// 流中是否包含符合条件的元素 类别:书籍,价格大于50
boolean anyMatch = list.stream().anyMatch(product -> product.getCategory().equals("书籍") && product.getPrice()>50);
System.out.println("是否包含价格大于50的书籍:"+anyMatch); // false
// 流中的商品数量是否都大于5
// allMatch方法,是否匹配所有元素; noneMatch方法,检查是否没有匹配所有元素
boolean allMatch = list.stream().allMatch(product -> product.getNum()>=5);
System.out.println("流中商品数量是否都大于5:"+allMatch); // false
}
/*
筛选(filter)与切片
筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作
*/
public static void filter(){
List<Product> list = getProduct();
// 筛选产品中价格大于100元或小于20元的产品,形成新的集合。collect收集
List<Product> pros = list.stream()
.filter(x->x.getPrice()>100 || x.getPrice()<20)
.collect(Collectors.toList());
pros.forEach(System.out::println);
System.out.println("--------");
// 去除重复元素,通过流所生成元素的hashCode()和equals()去除重复元素
Product product = new Product("ads","服装",259,1);
// 1、Collections.copy(新列表,源列表); 方法复制
/* asList获取的集合,使用add会报错,UnsupportedOperationException,我们需要再接收一下值
List<Product> products = Arrays.asList(new Product[list.size()]); 使用new ArrayList()<>报错
Collections.copy(products,list); // 将list元素复制到products 新列表的长度不能小于源列表长度;目标列表必须至少与源列表一样长。*/
// 2、addAll
/*List<Product> products = new ArrayList<>();
products.addAll(list);*/
// 3、用stream流复制列表
List<Product> products = list.stream().collect(Collectors.toList());
products.add(product);
products.add(product);
products.stream().distinct().forEach(System.out::println); // 输出只有一个ads
System.out.println("-------");
// limit(max)截断流,使元素不超过max个。
// skip(long n)跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit互补
// 取前2个电子产品
list.stream().filter(x->x.getCategory().equals("电子产品")).limit(2).forEach(System.out::println);
// 跳过前两个电子产品
list.stream().filter(x->x.getCategory().equals("电子产品")).skip(2).forEach(System.out::println);
}
/*
聚合 max/min/count
统计
*/
public static void aggregate(){
List<Product> list = getProduct();
// 取list中价格最高且数量大于10的产品
// max/min 要传入 Comparator.comparing(类名::方法名)
Optional<Product> max = list.stream().filter(x->x.getNum()>10).max(Comparator.comparing(Product::getPrice));
System.out.println("list中价格最高且数量大于10的产品:"+max.get()); // book2
// 筛选单价最低的产品
Optional<Product> min = list.stream().min(Comparator.comparing(Product::getPrice));
System.out.println("list中价格最低的商品:"+min.get()); // picture
// 计算流中总数
System.out.println(list.stream().count()); // 12
List<Integer> nums = Arrays.asList(7, 6, 9, 4, 11, 6);
// 自然排序
Optional<Integer> max1 = nums.stream().max(Integer::compareTo);
// 自定义排序(从大到小排序)
Optional<Integer> max2 = nums.stream().max((o1, o2) -> o2 - o1); // o2-o1>0,逆序。自定义,小的大
System.out.println("自然排序的最大值:" + max1.get()); // 11
System.out.println("自定义排序的最大值:" + max2.get()); // 4
}
/*
映射 map/flatMap/mapToInt/mapToDouble/mapToLong
映射可以将一个流的元素按照一定的映射规则映射到另一个流中。
|- map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射为一个新元素。
|- flatMap:接收一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有流连接成一个流。
|- mapToInt:接收一个函数作为参数,该函数会被应用到每个元素上,然后返回一个IntStream
*/
public static void map(){
// 将字符串数组全部改为大写。
char[] charArr = {'a','c','e','g'};
String newStr = new String(charArr); // 字符数组转为字符串1
String newStr2 = String.valueOf(charArr); // 字符数组转为字符串2
char[] chars = newStr2.toCharArray(); // 字符串转字符数组
String newStr3 = Arrays.toString(chars); // 数组转字符串 ['a','c','e','g']
String[] strArr = newStr2.split(""); // 字符串转为字符数组
List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
String listStr = strList.stream().collect(Collectors.joining(",")); // 如果joinin不传参或传“”,则list元素拼接
System.out.println(listStr); // A,C,E,G
// 整数数组每个元素+3
List<Integer> intList = Arrays.asList(1,3,5,9);
List<Integer> intList2 = intList.stream().map(x -> x + 3).collect(Collectors.toList());
System.out.println(intList2); // [4,6,8,12]
// 每个商品的数量+1
List<Product> products = getProduct();
products = products.stream().map(product->{
product.setNum(product.getNum()+1);
product.setNum(product.getNum()+1);
return product;
}).collect(Collectors.toList());
products.forEach(System.out::println);
// flatMap用法同map,只是执行过程有区别
// mapToInt 返回一个新流IntStream,后序可以做很多操作
List<String> strs = Arrays.asList("hi","nihaoa","666","hey");
// 获取字符串数组中每个字符串的长度
IntStream intStream = strs.stream().mapToInt(String::length);
//System.out.println("长度最大值:"+intStream.max().getAsInt()); // 6 流只能用一次
//System.out.println("长度平均值:"+intStream.average().getAsDouble()); // 3.5
System.out.println("长度之和:"+intStream.sum()); // 14
}
/*
规约 reduce
规约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积、求最值操作
*/
public static void reduce(){
List<Integer> countList = Arrays.asList(1,3,2,8,11,4);
// 求和1
Optional<Integer> sum1 = countList.stream().reduce((x,y)->x+y);
// 求和2
Optional<Integer> sum2 = countList.stream().reduce(Integer::sum);
// 求和3
Integer sum3 = countList.stream().reduce(0,Integer::sum);
// 求乘积
Optional<Integer> chengji = countList.stream().reduce((x,y)->x*y);
// 求最大值方式
Optional<Integer> max1 = countList.stream().reduce((x,y) -> x>y?x:y);
// 求最大值方式
Integer max2 = countList.stream().reduce(1,Integer::max);
System.out.println("和:"+sum3);
System.out.println("乘积:"+chengji.get());
System.out.println("最大值:"+max2);
List<Product> list = getProduct();
// 求价格和1
Optional<Integer> sumPrice = list.stream().map(Product::getPrice).reduce(Integer::sum);
// 求价格和2
Integer sumPrice2 = list.stream().reduce(0,(sum,p) -> sum+=p.getPrice(),(x,y)->x+y);
// 求价格和3
Integer sumPrice3 = list.stream().reduce(0,(sum,p) -> sum+=p.getPrice(),Integer::sum);
// 求最高工资方式1
Integer maxPrice1 = list.stream().reduce(0,(max,p) -> max > p.getPrice()?max:p.getPrice(),Integer::max);
// 求最高工资方式2
Integer maxPrice2 = list.stream().reduce(0,(max,p) -> max > p.getPrice()?max:p.getPrice(),(x,y)->x>y?x:y);
// 求最高工资方式3
Optional<Integer> maxPrice3 = list.stream().map(Product::getPrice).reduce(Integer::max);
System.out.println("最高价格:"+maxPrice3.get());
}
/*
收集collect
collect,收集,可以说是内容最繁多、功能最丰富的部分了。
从字面上理解,就是把一个流收集起来,最终收集成一个值也可以收集成一个新的集合。
collect主要依赖与java.util.stream.Collectors类内置的静态方法
*/
/*
归集 toList toSet toMap
因为流不存储数据,那么在流中的数据处理完成后,需要将流中的数据重新归集到新的集合里。
toList、toSet、toMap比较常用,另外还有toCollection、toConcurrentMap等复杂的用法
下面演示 toList toSet toMap
*/
public static void toListOrSet(){
List<Integer> countList = Arrays.asList(1,2,3,4,4,5,6,10,2);
// 筛选出偶数,放到新的集合里
List<Integer> doubleNum = countList.stream().filter(x->x%2==0).collect(Collectors.toList());
System.out.println(doubleNum);
// countList去重1,放到set
List<Integer> countListCopy = Arrays.asList(1,2,3,4,4,5,6,10,2);
//Set<Integer> set = countListCopy.stream().collect(Collectors.toSet());
//System.out.println(set);
// countList去重2
List<Integer> dis = countListCopy.stream().
distinct().collect(Collectors.toList());
System.out.println(dis);
List<Product> list = getProduct();
// 去除价格低于50和高于4000的商品,存到map
Map<String,Product> map = list.stream().filter(p->p.getPrice()>50 && p.getPrice()<4000).collect(Collectors.toMap(Product::getName,p->p));
System.out.println(map);
}
/*
统计(count/averaging)
Collectors提供了一系列用于数据统计的静态方法
计数:count
平均值:averagingInt、averagingLong、averagingDouble
最值:maxBy、minBy
求和:summingInt、summingLong、summingDouble
统计以上所有:summarizingInt、summarizingLong、summarizingDouble
*/
public static void count(){
List<Product> list = getProduct();
// 求总数
Long count = list.stream().collect(Collectors.counting());
Long count2 = list.stream().count();
// 求平均价格
Double average = list.stream().collect(Collectors.averagingDouble(Product::getPrice));
// 求最高价格
Optional<Integer> maxPrice = list.stream().map(Product::getPrice).collect(Collectors.maxBy(Integer::compare));
Integer maxPrice2 = list.stream().max(Comparator.comparing(Product::getPrice)).get().getPrice();
Integer maxPrice3 = list.stream().map(Product::getPrice).max(Integer::compareTo).get();
// 求价格之和
Integer priceAll = list.stream().collect(Collectors.summingInt(Product::getPrice));
// 一次性统计所有信息
DoubleSummaryStatistics collect = list.stream().collect(Collectors.summarizingDouble(Product::getPrice));
System.out.println("价格总数:"+count);
//System.out.println("平均价格:"+average);
System.out.println("平均价格:"+String.format("%.2f",average));
System.out.println("最高价格:"+maxPrice3);
System.out.println("价格之和:"+priceAll);
System.out.println("所有信息:"+collect); // collect.getMax()....
// 所有信息:DoubleSummaryStatistics{count=12, sum=21481.000000, min=12.000000, average=1790.083333, max=10000.000000}
}
/*
分组(partitioningBy/groupingBy)
分区:
将stream按条件分为两个Map,比如按商品价格是否高于1000分为两部分
分组:
将集合分为多个Map,比如商品按类别分组。有单级分组和多级分组
*/
public static void group(){
List<Product> list = getProduct();
// 将商品价格按1000分组
Map<Boolean,List<Product>> part = list.stream().collect(Collectors.partitioningBy(x->x.getPrice()>1000));
System.out.println("按价格分组:"+part);
// 商品按类别分组
Map<String,List<Product>> group = list.stream().collect(Collectors.groupingBy(Product::getCategory));
System.out.println("按照类别分组:"+group);
System.out.println("电子产品:"+group.get("电子产品"));
// 商品先按类别分类,再按价格分类
Map group2 = list.stream().collect(Collectors.groupingBy(Product::getCategory,Collectors.groupingBy(Product::getPrice)));
System.out.println(group2);
}
/*
接合(joining)
joining可以将stream中的元素用特定的连接符(没有的话,直接连接)连接成一个字符串
*/
public static void joining(){
List<Product> list = getProduct();
// 商品所有名字连接成字符串
String names = list.stream().map(p->p.getName()).collect(Collectors.joining(","));
System.out.println("所有名字:"+names);
}
/*
规约(reducing)
Collectors类提供的reducing方法,相比于stream本身的reduce方法
*/
public static void reducing(){
List<Product> list = getProduct();
// 每个商品的价格扣税
list.stream().collect(Collectors.reducing(0,Product::getPrice,(i,j)->(i+j-10)));
// stream的reduce
Optional<Integer> sum = list.stream().map(Product::getPrice).reduce(Integer::sum);
System.out.println("商品总价:"+sum.get());
}
/*
排序sorted
中间操作。有两种排序:
1、sorted(),自然排序,流中元素需要实现Comparable接口
2、sorted(Comparator com):Comparator排序器自定义排序
*/
public static void sorted(){
List<Product> list = getProduct();
// 按商品价格升序排序(自然排序)
List<Product> sorted1 = list.stream().sorted(Comparator.comparing(Product::getPrice)).collect(Collectors.toList());
System.out.println("价格升序:"+sorted1);
// 价格降序
List<Product> sorted2 = list.stream().sorted(Comparator.comparing(Product::getPrice).reversed()).collect(Collectors.toList());
System.out.println("价格降序:"+sorted2);
// 先按价格再按数量升序排序
List<String> sorted3 = list.stream()
.sorted(Comparator.comparing(Product::getPrice).thenComparing(Product::getNum))
.map(Product::getName)
.collect(Collectors.toList());
System.out.println(sorted3);
// 自定义排序 降序
List<Product> sorted4 = list.stream().sorted((p1,p2)->{
return p2.getPrice()- p1.getPrice(); //正降负升
}).collect(Collectors.toList());
System.out.println(sorted4);
}
/*
提取/组合
流也可以进行合并、去重、限制、跳过等操作
distinct去重,skip跳过,limit限制
*/
public static void limit(){
String[] arr1 = {"a","b","c","d"};
String[] arr2 = {"d","e","f","g","h"};
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// concat:合并两个流;distinct:去重
List<String> concatList = Stream.concat(stream1,stream2).distinct().collect(Collectors.toList());
// list拼接成字符串
String concatStr = concatList.stream().collect(Collectors.joining("-"));
System.out.println("拼接成的字符串:"+concatStr); // a-b-c-d-e-f-g-h
// limit:限制流中获得前n个数据
List<String> limitList = concatList.stream().limit(3).collect(Collectors.toList());
System.out.println("前三个元素:"+limitList); // [a, b, c]
// skip:跳过前n个数据
List<String> skipList = concatList.stream().skip(6).collect(Collectors.toList());
System.out.println("跳过前6个元素:"+skipList); // [g, h]
// 模拟分页 .skip((long) (pageNum - 1) * pageSize).limit(pageSize)
List<Product> list = getProduct();
// 获取第2页,每页3个
List<Product> pageList = list.stream().skip((2-1)*3).limit(3).collect(Collectors.toList());
System.out.println("第2页,3个:"+pageList);
}
/* 测试 */
public static void main(String[] args) {
// 创建流
//createStream();
// 遍历 匹配
//ForeachAndFind();
// 过滤
//filter();
// 聚合 总数/最大/最小
//aggregate();
// 映射
//map();
// reduce规约
//reduce();
// 归集
//toListOrSet();
// 统计
//count();
// 分组
//group();
// 接合
//joining();
// 归约 reducing
//reducing();
// 排序
//sorted();
// 提取/组合
limit();
}
}
class Product{
private String name; // 商品名
private String category; // 类别
private int price; // 单价
private int num; // 下单数量
public Product(String name, String category, int price, int num) {
this.name = name;
this.category = category;
this.price = price;
this.num = num;
}
public Product() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", price=" + price +
", num=" + num +
", category=" + category +
'}';
}
}