赞
踩
我们都知道,Stream是一组用来处理数组,集合的API;
日常工作中,我们使用Stream流的频率是非常高的,尤其是当我们迭代之前的代码,我们将看到许多,但是习惯了for和if、else的我,却对这些骚操作表示不容易一看便懂。正所谓识时务者为俊杰,我们也得好好研究研究。
我们先来看看官方文档里面的方法(非全部)。
返回类型 | 方法名 | 作用 |
---|---|---|
boolean | allMatch(Predicate<? super T> predicate) | 返回此流的所有元素是否与提供的谓词匹配 |
boolean | anyMatch(Predicate<? super T> predicate) | 返回此流的任何元素是否与提供的谓词匹配 |
static < T> Stream.Builder< T> | builder() | 返回一个 Stream生成器 |
< R> R | collect(Supplier< R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner) | 对此流的元素执行 mutable reduction操作 |
static < T> Stream< T> | concat(Stream<? extends T> a, Stream<? extends T> b) | 创建一个懒惰连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素 |
long | count() | 返回此流中的元素数 |
Stream< T> | distinct() | 返回由该流的不同元素(根据 Object.equals(Object) )组成的流 |
default Stream< T> | dropWhile(Predicate<? super T> predicate) | 如果此流被排序,则返回一个流,该流包含该流的剩余元素,在丢弃与给定谓词匹配的元素的最长前缀之后 |
Stream< T> | filter(Predicate<? super T> predicate) | 返回由与此给定谓词匹配的此流的元素组成的流 |
Optional< T> | findAny() | 返回Optional描述流的一些元件,或一个空Optional如果流是空的 |
Optional< T> | findFirst() | 返回Optional描述此流的第一个元素,或空Optional如果流是空的 |
void | forEach(Consumer<? super T> action) | 对此流的每个元素执行操作 |
Stream< T> | limit(long maxSize) | 返回由此流的元素组成的流,截短长度不能超过 maxSize |
Optional< T> | max()/min() | 根据提供的 Comparator返回此流的最大/小元素 |
static < T> Stream< T> | of(T t) | 返回包含单个元素的序列 Stream |
Stream< T> | peek(Consumer<? super T> action) | 返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作 |
Optional< T> | reduce(BinaryOperator< T> accumulator) | 返回描述减小值(如果有)的 Optional |
Stream< T> | sorted(Comparator<? super T> comparator) | 返回由该流的元素组成的流,根据提供的 Comparator进行排序 |
Object[] | toArray() | 返回一个包含此流的元素的数组 |
< A> A[] | toArray(IntFunction<A[]> generator) | 使用提供的 generator函数返回一个包含此流的元素的数组,以分配返回的数组,以及分区执行或调整大小可能需要的任何其他数组 |
终结方法:一旦Stream调用了终结方法,流的操作就全部终结了,不能继续使用,
只能创建新的Stream操作。
终结方法: foreach , count。
非终结方法:每次调用完成以后返回一个新的流对象,可以继续使用,支持链式编程。
@Data @AllArgsConstructor @NoArgsConstructor @ToString public class Account { //账户人名 private String name; //账户类型 private int AccountType; //账户状态 1:有效,0:无效 private int status; //账户余额 private BigDecimal money; //创建时间 private LocalDate createTime; }
public enum AccountType {
ORDINARY_ACCOUNT(1,"普通账户"),
SILVER_ACCOUNT(2,"白银账户"),
DIAMOND_ACCOUNT(3,"钻石账户"),
OWN_ACCOUNT(4,"自己人账户");
private int code;
private String message;
private AccountType(int code,String message){
this.code=code;
this.message=message;
}
}
-固定的数据:
public class AccountTest {
private List<Account> list;
private void build(){
Account account = new Account("单边李",4,1,new BigDecimal("1000000.00"), LocalDate.of(2020,1,1));
Account account2 = new Account("唐银箭",1,1,new BigDecimal("10.00"), LocalDate.of(2020,2,1));
Account account3 = new Account("赵香悦",2,1,new BigDecimal("100.00"), LocalDate.of(2020,3,1));
Account account4 = new Account("皮卡法香",3,1,new BigDecimal("1000.00"), LocalDate.of(2020,4,1));
Account account5 = new Account("今晚打老虎",1,1,new BigDecimal("1.00"), LocalDate.of(2020,5,1));
Account account6 = new Account("史提芬周",2,0,new BigDecimal("0.01"), LocalDate.of(2020,6,1));
list= Arrays.asList(account,account2,account3,account4,account5,account6);
}
}
过滤,剩下的name不等于“单边李”的数据(结果无单边李):
@Test
public void method(){
build();
list.stream().filter(item->!"单边李".equals(item.getName())).forEach(item-> System.out.println(item));
}
//结果
Account(name=唐银箭, AccountType=1, status=1, money=10.00, createTime=2020-02-01)
Account(name=赵香悦, AccountType=2, status=1, money=100.00, createTime=2020-03-01)
Account(name=皮卡法香, AccountType=3, status=1, money=1000.00, createTime=2020-04-01)
Account(name=今晚打老虎, AccountType=1, status=1, money=1.00, createTime=2020-05-01)
Account(name=史提芬周, AccountType=2, status=0, money=0.01, createTime=2020-06-01)
过滤,剩下的创建账户时间大于2020-05-01的数据(结果大于2020-05-01)
@Test
public void method(){
build();
list.stream().filter(item->item.getCreateTime().isAfter(LocalDate.of(2020,5,1))).forEach(item-> System.out.println(item));
}
//结果
Account(name=史提芬周, AccountType=2, status=0, money=0.01, createTime=2020-06-01)
@Test
public void method(){
build();
list.stream().map(Account::getName).forEach(item-> System.out.println(item));
}
//结果
单边李
唐银箭
赵香悦
皮卡法香
今晚打老虎
史提芬周
比如要对数据库返回的数据做转化类型,便于给前端使用。
前端返回类型:
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class AccountReturn {
private String name;
private BigDecimal money;
}
转换类型的静态方法:
public class Transform {
//Account类型转为AccountReturn类型方法
public static AccountReturn trans(Account account){
return new AccountReturn(account.getName(),account.getMoney());
}
}
stream.map中调用
@Test
public void method(){
build();
list.stream().map(Transform::trans).forEach(System.out::println);
//list.stream().map(item->Transform.trans(item)).forEach(System.out::println);//一样
//结果
AccountReturn(name=单边李, money=1000000.00)
AccountReturn(name=唐银箭, money=10.00)
AccountReturn(name=赵香悦, money=100.00)
AccountReturn(name=皮卡法香, money=1000.00)
AccountReturn(name=今晚打老虎, money=1.00)
AccountReturn(name=史提芬周, money=0.01)
}
不建议使用foreach方法来调用静态方法,因为是终结方法
@Test public void method(){ build(); //根据账户类型升序 list.stream().sorted(Comparator.comparing(Account::getAccountType)).forEach(System.out::println); System.out.println("------------------"); //账户类型降序 list.stream().sorted(Comparator.comparing(Account::getAccountType).reversed()).forEach(System.out::println); } //结果 Account(name=唐银箭, AccountType=1, status=1, money=10.00, createTime=2020-02-01) Account(name=今晚打老虎, AccountType=1, status=1, money=1.00, createTime=2020-05-01) Account(name=赵香悦, AccountType=2, status=1, money=100.00, createTime=2020-03-01) Account(name=史提芬周, AccountType=2, status=0, money=0.01, createTime=2020-06-01) Account(name=皮卡法香, AccountType=3, status=1, money=1000.00, createTime=2020-04-01) Account(name=单边李, AccountType=4, status=1, money=1000000.00, createTime=2020-01-01) ------------------ Account(name=单边李, AccountType=4, status=1, money=1000000.00, createTime=2020-01-01) Account(name=皮卡法香, AccountType=3, status=1, money=1000.00, createTime=2020-04-01) Account(name=赵香悦, AccountType=2, status=1, money=100.00, createTime=2020-03-01) Account(name=史提芬周, AccountType=2, status=0, money=0.01, createTime=2020-06-01) Account(name=唐银箭, AccountType=1, status=1, money=10.00, createTime=2020-02-01) Account(name=今晚打老虎, AccountType=1, status=1, money=1.00, createTime=2020-05-01)
优先级:账户状态->账户类型->账户余额
账户状态降序(先展示有效),账户类型升序,余额升序
@Test public void method(){ build(); //优先级:账户状态->账户类型->账户余额 //账户状态降序(先展示有效),账户类型升序,余额升序 list.stream() .sorted(Comparator.comparing(Account::getStatus).reversed() .thenComparing(Account::getAccountType) .thenComparing(Account::getMoney)) .forEach(System.out::println); } //结果 Account(name=今晚打老虎, AccountType=1, status=1, money=1.00, createTime=2020-05-01) Account(name=唐银箭, AccountType=1, status=1, money=10.00, createTime=2020-02-01) Account(name=赵香悦, AccountType=2, status=1, money=100.00, createTime=2020-03-01) Account(name=皮卡法香, AccountType=3, status=1, money=1000.00, createTime=2020-04-01) Account(name=单边李, AccountType=4, status=1, money=9999.00, createTime=2020-01-01) Account(name=单边李, AccountType=4, status=1, money=1000000.00, createTime=2020-01-01) Account(name=史提芬周, AccountType=2, status=0, money=0.01, createTime=2020-06-01)
注意1,thenComparing是对上一个排序结果的再排序,如果重复使用reversed方法,可能会出现逻辑错误!
可以使用
.thenComparing(Account::getMoney,Comparator.reverseOrder())
来解决重复需要重复降序的问题
注意2
如果前一个排序条件都为null,则可能会报错:如下例子
List<Persion> list=new ArrayList<>();
list.add(new Persion(null,"a",2));
list.add(new Persion(null,"b",2));
list.add(new Persion(null,"c",3));
list.add(new Persion(null,"d",3));
list.add(new Persion(null,"e",null));
list.stream()
.sorted(Comparator.comparing(Persion::getId).thenComparing(Persion::getAge))
.forEach(System.out::println);
报错:
at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
at java.util.Comparator.lambda$thenComparing$36697e65$1(Comparator.java:216)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
at java.util.TimSort.sort(TimSort.java:220)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:348)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream...
解决方案:
public static void main(String[] args) { List<Persion> list=new ArrayList<>(); list.add(new Persion(1,"a",null)); list.add(new Persion(1,"b",2)); list.add(new Persion(1,"b",null)); list.add(new Persion(2,"c",3)); list.add(new Persion(4,"d",null)); list.add(new Persion(3,"e",1)); //null放在后面 list.stream() .sorted(Comparator.comparing(Persion::getId). thenComparing(Persion::getAge,Comparator.nullsLast(Integer::compareTo)). thenComparing(Persion::getName)) .forEach(System.out::println); System.out.println("-----------------"); //null放在前面 list.stream() .sorted(Comparator.comparing(Persion::getId). thenComparing(Persion::getAge,Comparator.nullsFirst(Integer::compareTo)). thenComparing(Persion::getName)) .forEach(System.out::println);
@Test public void method(){ build(); //List转为Set Set<Account> collectSet = list.stream().collect(Collectors.toSet()); collectSet.stream().forEach(System.out::println); System.out.println("--------------"); //Set转为List collectSet.stream().collect(Collectors.toList()).forEach(System.out::println); } //结果 Account(name=史提芬周, AccountType=2, status=0, money=0.01, createTime=2020-06-01) Account(name=皮卡法香, AccountType=3, status=1, money=1000.00, createTime=2020-04-01) Account(name=今晚打老虎, AccountType=1, status=1, money=1.00, createTime=2020-05-01) Account(name=唐银箭, AccountType=1, status=1, money=10.00, createTime=2020-02-01) Account(name=单边李, AccountType=4, status=1, money=1000000.00, createTime=2020-01-01) Account(name=赵香悦, AccountType=2, status=1, money=100.00, createTime=2020-03-01) -------------- Account(name=史提芬周, AccountType=2, status=0, money=0.01, createTime=2020-06-01) Account(name=皮卡法香, AccountType=3, status=1, money=1000.00, createTime=2020-04-01) Account(name=今晚打老虎, AccountType=1, status=1, money=1.00, createTime=2020-05-01) Account(name=唐银箭, AccountType=1, status=1, money=10.00, createTime=2020-02-01) Account(name=单边李, AccountType=4, status=1, money=1000000.00, createTime=2020-01-01) Account(name=赵香悦, AccountType=2, status=1, money=100.00, createTime=2020-03-01)
我又新加了条name也为“单边李”的数据,
Account account1 = new Account("单边李",4,1,new BigDecimal("9999.00"), LocalDate.of(2020,1,1));
public void method(){ build(); //List转为Set //Map<String, Account> accountMap = list.stream().collect(Collectors.toMap(Account::getName,account-> account)); //System.out.println(accountMap.get("单边李")); //有重复key的时候,Function.identity()返回对象本身 Map<String, Account> accountMap2 = list.stream().collect(Collectors.toMap(Account::getName, Function.identity(),(account1,account2)-> account1)); //取单个字段时,有重复key Map<String, BigDecimal> accountMap3 = list.stream().collect(Collectors.toMap(Account::getName, Account::getMoney, (account1, account2) -> account1)); System.out.println(accountMap3.get("单边李")); //如果value有多个,value用“,”拼接 Map<Integer, String> map = list.stream().collect(Collectors.toMap(Account::getAccountType,Account::getName,(v1,v2)->v1+","+v2)); System.out.println(map.get(4)); //根据name分组 Map<String, List<Account>> collect = list.stream().collect(Collectors.groupingBy(Account::getName)); System.out.println(collect.get("单边李")); } //结果 1000000.00 单边李,单边李 [Account(name=单边李, AccountType=4, status=1, money=1000000.00, createTime=2020-01-01), Account(name= 单边李, AccountType=4, status=1, money=9999.00, createTime=2020-01-01)]
OPtional类为了优雅的解决结果集的空指针异常,不用每次都去前面if判断非空
如:
private List<Account> list; private List<Account> list2; private void build(){ Account account = new Account("AAA",1,1,new BigDecimal("1"),LocalDate.of(2020,1,1)); Account account1 = new Account("单边李",4,1,new BigDecimal("9999.00"), LocalDate.of(2020,1,1)); Account account2 = new Account("唐银箭",1,1,new BigDecimal("10.00"), LocalDate.of(2020,2,1)); Account account3 = new Account("赵香悦",2,1,new BigDecimal("100.00"), LocalDate.of(2020,3,1)); Account account4 = new Account("皮卡法香",3,1,new BigDecimal("1000.00"), LocalDate.of(2020,4,1)); Account account5 = new Account("今晚打老虎",1,1,new BigDecimal("1.00"), LocalDate.of(2020,5,1)); Account account6 = new Account("史提芬周",2,0,new BigDecimal("0.01"), LocalDate.of(2020,6,1)); list= Arrays.asList(null,account,account1,account2,account3,account4,account5,account6); //新增一个集合list2,只有两条数据 list2=Arrays.asList(account,account1); } @Test public void method(){ build(); //如果ofNullable(T t)t为null,则返回orElse中的数据 List<Account> accounts = (List<Account>) Optional.ofNullable(null).orElse(list2); accounts.forEach(System.out::println); } //结果: Account{name='AAA', AccountType=1, status=1, money=1, createTime=2020-01-01} Account{name='单边李', AccountType=4, status=1, money=9999.00, createTime=2020-01-01}
结果集为空直接可以报错来终止程序
try {
Optional.ofNullable(null).orElseThrow(()->new Exception("不能为空"));
} catch (Exception e) {
e.printStackTrace();
}
//结果:
java.lang.Exception: 不能为空
at cn.danbianli.dto.AccountTest.lambda$method$0(AccountTest.java:31)
at java.util.Optional.orElseThrow(Optional.java:290)
.....
链式编程的随意结合,如
需求:
需要账户有效的,按照余额倒序排列的给前端使用的最多1000条记录的,可以根据输入账户name查询的方法,如果有人名冲突,则只展示最多的账户
@Test
public void method(){
build();
Map<String, AccountReturn> map = list.stream().filter(item -> item.getStatus() != 0)
.sorted(Comparator.comparing(Account::getMoney).reversed())
.map(Transform::trans)
.limit(1000)
.collect(Collectors.toMap(AccountReturn::getName, Function.identity(), (account1, account2) -> account1));
System.out.println(map.get("单边李"));
}
//结果
AccountReturn(name=单边李, money=1000000.00)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。