Java-Lambda和Stream的使用大全(案例特全)

x33g5p2x  于10个月前 转载在 Java  
字(28.7k)|赞(0)|评价(0)|浏览(103)

介绍

Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda表达式的运用更加方便、高效。

说白了其实还是 匿名内部类 就是少写点代码而已 匿名类部类的作用就是将接口重写和对方法的参数进行赋值

比如:

interface lambda_inter {
        void show(String str);

    }

我们来使用匿名内部类重写 这个接口

@Test
    public void show(){
        lambda_inter a= new lambda_inter() {
            @Override
            public void show(String str) {
                System.out.println(str);
            }
        };
        a.show("11111111111111");
    }

结果:11111111111111

我们还可以使用此接口作为方法的参数然后利用匿名内部类 来传入

我们随便在一个类中定义一个方法

public void abs(String str ,lambda_inter  a){
        a.show(str);
    }

然后使用匿名内部类来传入参数

@Test
    public void show(){
        abs("11111111", new lambda_inter() {
            @Override
            public void show(String str) {
                System.out.println(str);
            }
        });
    }

结果:11111111

通过以上代码我们可以发现 每次使用匿名内部类的使用都要重写方法能不能直接就输出方法内的代码呢?? 这就用到了我们的java.8里的新特性

我们先来了解下什么是函数编程式思想

面向对象的思想:

做一件事,找一个能解决这个事情对象 调用对象的方法 完成事情

函数式编程思想:

只要能获取到结果,谁去做,怎么做,都不重要 我只要结果

做什么,而不是怎么做

我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做的事情是:执行对象内方法中的代码 这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那,有没有更加简单的办法?如果我们将关注点从"怎么做"回归到"做什么"的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要。

比如:

当我们需要从北京到上海时,可以选择高铁、汽车、骑行或是徒步。我们的真正目的是到达上海,而如何才能到达上海的形式并不重要,所以我们一直在探索有没有比高铁更好的方式-搭乘飞机。

而现在这种飞机(甚至是飞船)已经诞生: 2014年3月Oracle所发布的Java 8 (JDK 1.8 )中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。

而且还不需要 其他类来继承接口 我们就可以通过其他类已经实现了的代码 使用接口来调用 但是要求是接口中的方法的参数必须一致 下面会具体来说

Lambda语法

  • 一个参数
  • 一个箭头
  • 一段代码

(参数) -> {代码块}

就是这些符号就能帮我们省略匿名类内部类的大部分代码

我们解释下这些符号的意思

前面的小括号就是方法的参数列表

中间的箭头就是将小括号中的参数传递给后面的代码块

而代码块就是重写抽象方法的方法体简称写代码的地方

我们使用Lambda将这些框起来的代码给省略掉

接口还是一样

interface lambda_inter {
    void show(int str);
}

使用Lambda重写接口中的方法

@Test
    public void xians(){
        lambda_inter inter=(String str)->{
            System.out.println(str);
        };
            inter.show("111111");
    }

结果:111111

是不是少了很多代码 当然下面还有更少的呢

//最简化版
        lambda_inter inter1=str->System.out.println(str);
        inter1.show("2222222");
至于简化规则 下面会慢慢 讲述

是不是 不再有 不得不创建接口对象的束缚, 不再有 抽象方法覆盖重写的负担,就是这么简单!

Lambda规则

使用Lambda必须是一个接口而且 一个接口中只能有一个抽象方法 (简称:函数式接口) 只有这样才能使用Lambda来重写抽象方法

可以使用 @FunctionalInterface 注解 来声明接口类 强制要求接口中的抽象方法只能有一个
1.
方法的参数类型可以省略 直接写 名称就行 (自动推导类型)
1.
方法无参使用() 1个参数可以省略( ) 直接名称,多个参数必须加上括号(a,b,c)
1.
方法体内就一条语句的时候 可以省略 { } , 返回值 和 语句结尾的分号

这里注意如果有返回值那么这一条语句的结果就是返回值
1.
Lambda中的方法参数名 不能和局部变量名相同
1.
在Lambda中不能改变部局部基本数据类型的值 包括 包装类型和字符串 但是 可以改变引用类型的值比如集合数组 还可以改变成员变量 至于为什么 因为在方法中的基本类型变量都是栈里的 所以方法执行完就消失 但是内部类(Lambd) 和 成员变量 以及其他对象 都是在堆中和方法区 不会随着方法执行完而消失

掌握以上规则那么就可以任意使用Lambda了我们下面演示几条常用的语句

常用的 Lambda 语法

无参数,无返回值

@FunctionalInterface
interface lambda_inter {
    void show();

}
@Test
    public void xians1(){
        lambda_inter  inter= () -> System.out.println("Hello Lambda!"); //重写接口中的方法
        inter.show();//调用接口方法
    }

有一个参数,并且无返回值

@FunctionalInterface
interface lambda_inter {
    void show(String str);

}
@Test
    public void xians1(){
        //重写接口中的方法
        lambda_inter inter=x-> System.out.println(x);
        inter.show("111111");//调用接口方法
    }

多个参数,有返回值,多条语句

@FunctionalInterface
interface lambda_inter {
    String[] show(String str,String str1,String str2);
}
@Test
    public void xians1() {
        //重写接口中的方法
        lambda_inter inter = (a, b, c, num) -> {
            if (num < 3) {
                num = 3;
            }
            String[] str = new String[num];
            str[0] = a;
            str[1] = b;
            str[2] = c;
            return str;
        };
        //调用接口中的方法
        String[] show = inter.show("111", "222", "333", 3);
        for (String s : show) { //遍历数组
            System.out.println(s);
        }
    }

111
222
333

给方法赋值

public class test {
    @FunctionalInterface
    interface lambda_inter {
        int show(int a,int b);
    }

    public  void num(int a,int b,lambda_inter inter){
        System.out.println("两数相加的结果:"+inter.show(a,b));
    }

    @Test
    public void xians1(){
        //重写接口中的方法
        lambda_inter inter=(a,b)-> a+b;
        num(1,2,inter);
    }
}

Lambda中的:: 双分号使用

Java 8 中我们可以通过 :: 关键字来访问类的构造方法,对象方法,静态方法。

获取到这些方法后就能直接绑定到接口上不需要继承 和重写了,摆脱了类的负担

可以在其他的类中找到写好的代码只需要知道参数和返回值 找到对应的接口 直接就能拿来用

规则: 参数的个数类型顺序返回值必须一致,虽然返回值可以不一致但是会影响到方法的使用最终结果

访问静态方法

函数接口 name =Something::staShow;

访问对象

Something som = new Something();
函数接口 name =som::objShow;

或者 
函数接口 name =new Something()::objShow;

访问构造方法

无参和带参取决于接口中抽象方法参数的个数和对应构造匹配

函数接口 name = Something::new;

总结: 关于对象 都带括号 构造和静态都不带括号

访问本类方法

函数接口 name=this::show

访问父类方法

必须是继承关系

函数接口 name=super::show;

下面是一个小小的演示

package Lambda;

public class Obj_Class {
    private String str;

    public Obj_Class() {
        System.out.println("无参");
    }

    public Obj_Class(String str) {
        System.out.println("带参:"+str);
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public void show(String str,int age){
        System.out.println("你叫什么名字:___"+str+"今年多大了:__"+age);
    }
    public int num(Integer...a){
        int num=0;
        for (int i : a) {
            num+=i;
        }
        return num;
    }

}

我们将类的方法直接给接口无需重写和继承

为了接口能够重复使用我们定义万能接口类,内部定义泛型接口

public class Lambda {

    //访问无参构造
    @FunctionalInterface
    interface lambda {
        void show();
    }

    //无返回值的
    @FunctionalInterface
    interface lambda_1_V<A> {
        void show(A a);
    }

    //带返回值的
    @FunctionalInterface
    interface lambda_1_R<A, R> {
        R show(A a);
    }

    // 可变参数 不带返回值的
    @FunctionalInterface
    interface lambda_1_Par_V<A> {
        void show(A... a);
    }

    //可变参数 带返回值的
    @FunctionalInterface
    interface lambda_1_Par_R<A, R> {
        R show(A... a);
    }
    //二个参数
    //无返回值的
    @FunctionalInterface
    interface lambda_2_V<A, B> {
        void show(A a, B b);
    }
    //带返回值的
    @FunctionalInterface
    interface lambda_2_R<A, B, R> {
        R show(A a, B b);
    }
    // 可变参数 不带返回值的
    @FunctionalInterface
    interface lambda_2_Par_V<A> {
        void show(A... a);
    }
    //可变的 带返回值的
    @FunctionalInterface
    interface lambda_2_Par_R<A, B, R> {
        R show(A a, B... b);
    }
}

    ...........................省略无数个 自己按照上面 往下写

需要注意: 如果使用 接口中方法 带可变参数 那么使用此接口方法时候传入的可变类型必须是包装类 和包装数组 如果是一个一个值的添加 可以使用基本数据类型

列:

@Test
    public void xians1(){
      Lambda.lambda_2_V<String,Integer> lam=new Obj_Class()::show;
        lam.show("王五",22);
    }

以上只是部分让你体验下 lambda的强大之处,以后就不用写代码了 , 通过接口就能直接就能使用他的方法了也不需要继承 不用管它内部怎么实现的 我只要结果

这是一种简单的方法我们来点复杂的

使用上面的万能接口类我们模拟计算器计算

public class Obj_Class { 
public void num(int a,Lambda.lambda_1_Par_R<Integer,Integer> par){
        System.out.println("相加结果为:"+par.show(a));
    }
}

至于par.show(a,b,c) 如何计算的 我们不需要会,去百度找一下或者从以前写的代码中拿过来

比如我现在有一个以前 写好了的代码

public int num(Integer...a){
        int num=0;
        for (int i : a) {
            num+=i;
        }
        return num;
    }

那么我们将这代码 拿过来直接使用

@Test
    public void xians1(){
        //调用Obj_Class类的num方法
        Lambda.lambda_1_Par_R<Integer,Integer> par=new Obj_Class()::num;
        Integer show = par.show(1, 2, 3);
        System.out.println(show);
    }

6

但是在生活中光是使用肯定不行 我们还要根据需求进行扩展 这时候Lambda强大之处来了 我们根据上面的代码进行了 扩展 加了一些 验证功能

public int mathematics(  Lambda.lambda_1_Par_R<Integer,Integer> parR,Integer... a ){
        if (a.length==0){
            return 0;
        }
        for (Integer inte: a) {
                if (inte == 0){
                    return 0;
                }
        }
       return parR.show(a);//重点
    }
@Test
    public void xians1(){
        Lambda.lambda_1_Par_R<Integer,Integer> par=new Obj_Class()::num; //重点
        int mathematics = mathematics(par, 1, 2, 3, 4, 5, 6);
        System.out.println(mathematics);
    }

21

Lambda中的函数接口

为什么要用到函数式接口呢 ?

我们来看一个案例:

/** * * @param level 级别 * @param msg //日志内容 */
    public static void log(int level,String msg){
        //满足级别 打印日志内容
        if(level==1){
            System.out.println(msg);
        }
    }

    public static void main(String[] args) {
        String str1="Hello";
        String str2="World";
        String str3="Java";
        log(1,str1+str2+str3);
    }

可以发现上面案例中 我们是先对字符串拼接 后在进行判断是否满足日志级别

如果我把 日志级别 1 改为 2 ,if 内语句不执行, 那么我字符串是不是白拼接了,这样不就造成了性能浪费了吗

解决办法就是使用lambda表达式,lambda表达式是延迟执行的 这正好可以作为解决方案提升性能

//定义函数式接口
	@FunctionalInterface
    interface message {
        void show();
    }

    /** * @param level 级别 * @param msg //日志内容 */
    public static void log(int level, message msg) {
        //满足级别 打印日志内容
        if (level == 1) {
            //只有当满足条件后我们才会调用重写的 方法 进行字符串拼接
            msg.show();
        }
    }

    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "World";
        String str3 = "Java";
        log(1, () -> System.out.println(str1 + str2 + str3));
    }

HelloWorldJava

在上面的案例中函数式接口都是我们自己定义的 那么 java中有没有给我们提供呢??

答案当然有了 …

Supplier<T>接口

源码

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

可以看出来 就 是一个 无参数 带返回值 的 一个get 抽象方法

也就意味着这个接口主要的作用是对外提供数据的 等同于封装中的Get()用法

我们来简单演示下:

//定义一个方法 方法的参数传递Supplier<T>接口
    public static String  getString(Supplier<String> sup){
        //执行Supplier接口内的 get抽象方法 但是这个抽象方法需要被重写
        return  sup.get();
    }

    public static void main(String[] args) {
      String name=  getString(()-> "胡哥");
        System.out.println(name);
    }

胡哥

我们在来点复杂的:

获取数组中最大值

//定义一个方法 方法的参数传递Supplier<T>接口
    public static Integer  getString(Supplier<Integer> sup){
        //执行Supplier接口内的 get抽象方法 但是这个抽象方法需要被重写
        return  sup.get();
    }

    public static void main(String[] args) {
        Integer[] array={2,3,1,-10,100,44,66};
        Integer num=  getString(()->{
            int max=array[0];
            for (int i : array) {
                if ( max<i){
                    max=i;
                }
            }
            return  max;
        });
        System.out.println(num);
    }

100

Consumer<T>接口

我们先来看看源码在分析 他主要是干嘛的

源码:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

通过源码我们可以发现 accept 是一个 只有一个参数无返回值的抽象方法

那么 很好理解了 就是 将传进来的数据 进行处理 等同于封装中的Set()用法

那么andThen这个默认方法是干嘛的 ?

我们来分析下源码

Objects.requireNonNull(after);  //判断对象是否为空 如果是空 报异常
 return (T t) -> { accept(t); after.accept(t); };
//可以发现这段代码就是调用当前的方法 和 调用传递进来的接口内的方法 然后返回一个接口用于再次拼接

我们来演示下怎么使用的:

accept的使用方法

//定义一个方法 方法的参数传递一个String 和一个Consumer<T>接口
    public static void   method(String name,Consumer<String> con){
         con.accept(name);
    }

    public static void main(String[] args) {
        //将传递 进去的 数据 进行反转 打印输出
        method("abcd",name-> System.out.println(new StringBuilder(name).reverse()));

    }

dcba

accept配合andThen使用方法

//定义一个方法 方法的参数传递String 和 2个 Consumer<T>接口
    public static void   method(String name,Consumer<String> con,Consumer<String> con1){
        //先执行 con 在执行con1
         con.andThen(con1).accept(name);
    }

    public static void main(String[] args) {
        //将传递 进去的 数据 进行反转 打印输出
        method("abcd",
                //将字符串全部转换为 大写
                name-> System.out.println(name.toUpperCase()),
                //将字符串全部转换为 小写
                name-> System.out.println(name.toLowerCase())
        );
    }

ABCD
abcd

如果有 3 个或者4个参数类型是 Consumer接口 怎么办

con.andThen(con1).andThen(con2).andThen(con3).accept(name);  //以此类推

我们进行一个小练习:

"年龄,性别" 中的名称和性别分开打印

//定义一个方法 方法的参数传递一个String[] 和2个Consumer<T>接口
    public static void   method(String[] strarr,Consumer<String> con,Consumer<String> con1){
		//遍历集合
        for (String s : strarr) {
            //先执行 con 在执行con1
            con.andThen(con1).accept(s);
        }

    }

    public static void main(String[] args) {
        String[] strarr={"迪丽热巴,女","古力娜扎,女","马尔扎哈,男"};
        method(strarr,
                name-> {
                    System.out.println("姓名:"+name.split(",")[0]);
                },
                name->{
                    System.out.println("性别:"+name.split(",")[1]);
                }
        );
    }

姓名:迪丽热巴
性别:女
姓名:古力娜扎
性别:女
姓名:马尔扎哈
性别:男

Predicate<T>接口

有时候我们需要对某种类型的数据进行判断.从而得到一个Boolean值结果,

源码:

@FunctionalInterface
public interface Predicate<T> {  	
	
	boolean test(T t);
		
		// 等效 &&
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

	//等效!
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    //等效 ||
   default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    
}

我们来演示他们的用法

boolean test(T t); 用法

private  static  void method(Predicate<String> prd){
        boolean pd = prd.test("Helloworld");
        if(pd){
            System.out.println("字符串太长了");
        }else{
            System.out.println("字符串");
        }
    }

    public static void main(String[] args) {
            method(name->name.length()>3);
    }

字符串太长了

既然是条件判断 ,就会存在 与 或 非 这三种常见的逻辑关系

and

private  static  void method(Predicate<String> prd,Predicate<String> prd1){
        boolean pd = prd.and(prd1).test("Helloworld");
        if(pd){
            System.out.println("字符串符合要求");
        }else{
            System.out.println("字符串不符合要求");
        }
    }

    public static void main(String[] args) {
            method(name->name.startsWith("H"),name->name.contains("W"));
    }

字符串不符合要求

如果多个可以这样

private  static  void method(Predicate<String> prd,Predicate<String> prd1,Predicate<String> prd2){
        boolean pd = prd.and(prd1).and(prd2).test("Helloworld");
        if(pd){
            System.out.println("字符串符合要求");
        }else{
            System.out.println("字符串不符合要求");
        }
    }

    public static void main(String[] args) {
            method(name->name.startsWith("H"),name->name.contains("w"),name->name.length()>3);
    }

字符串符合要求

or

private  static  void method(Predicate<String> prd,Predicate<String> prd1){
        boolean pd = prd.or(prd1).test("Helloworld");
        if(pd){
            System.out.println("字符串符合要求");
        }else{
            System.out.println("字符串不符合要求");
        }
    }

    public static void main(String[] args) {
            method(name->name.startsWith("H"),name->name.contains("w"));
    }

字符串符合要求

negate

private  static  void method(Predicate<String> prd){
        boolean pd = prd.negate().test("Helloworld");
        if(pd){
            System.out.println("字符串符合要求");
        }else{
            System.out.println("字符串不符合要求");
        }
    }

    public static void main(String[] args) {
            method(name->name.startsWith("H"));
    }

字符串不符合要求

Function<T,R>接口

这个接口主要作用就是根据一个类型的数据得到另一个 类型的数据

源码:

@FunctionalInterface
public interface Function<T, R> {
      R apply(T t);
     default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
}
private  static  void method(String name,Function<String, Integer> Fun){
         Integer apply = Fun.apply(name);
         System.out.println(apply+10);
    }

    public static void main(String[] args) {
            method("100", Integer::parseInt);
    }

110

我们可以使用 Function<T, R> 接口中的 andThen 将多个结果拼接

private  static  void method(String name,Function<String, Integer> Fun,Function<Integer, Integer> Fun1){
         Integer apply = Fun.andThen(Fun1).apply(name);
         System.out.println(apply+10);
    }

    public static void main(String[] args) {
            method("100", Integer::parseInt,name->name+100);
    }

210

Stream流

说Stream流之前我们先来说一说 循环遍历的弊端

for 循环的语句就是怎么做
1.
for循环的循环体才是做什么

为什么使用循环?,因为要进行遍历.但循环是遍历的唯一方式吗? 遍历是指每一个元素逐一进行处理.

试想一下,如果希望对集合中的元素进行筛选过滤:

  1. 将集合A根据条件一过滤为子集合B;
  2. 然后在根据条件二过滤为子集合C;

那么怎么办? 在java8之前的做法可能为:

public static void main(String[] args) {
        //创建一个List集合 存储名称
        List<String> list =new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
    //对list 集合中元素进行过滤 ,值要以张开头的元素,存储到一个新接种
        List<String> listA =new ArrayList<>();
        for (String s : list) {
            if(s.startsWith("张")){
                listA.add(s);
            }
        }
    //对list 集合进行过滤,只要名称长度为3的人 存储对一个新集合中
        List<String> listB =new ArrayList<>();
        for (String s : listA) {
            if(s.length()==3){
                listB.add(s);
            }
        }
    //遍历集合
            for (String s : listB){
                System.out.println(s);
            }

    }

张无忌
张三丰

这段代码中包含三个循环 :

  1. 第一个循环 过滤开头为张的值 然后存储到新集合中A
  2. 第二个循环 我们在对这一个新集合A进行遍历 过滤长度为3的值 存储到新集合B
  3. 第三个循环 然后打印集合B内的值

每当我们需要对集合中的元素进行操作的时候,总需要进行 循环 循环 在循环 有没有更加简单的方式?

Lambda的衍生物Stream能给我们简化上面的操作

public static void main(String[] args) {
        //创建一个List集合 存储名称
        List<String> list =new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        list.stream()
                .filter(name->name.startsWith("张"))
                .filter(name->name.length()==3)
                .forEach(System.out::println);
        
    }

张无忌
张三丰

可以看到是不是简便了很多

我们来说一说 Stream的规则

在Stream流中有很多方法 ,但是这些方法 可以被分为两种

延迟方法: 返回值类型 任然是Stream接口自身类型的方法 ,因此支持链式调用

包括: filter map limit skip
1.
终结方法: 返回值类型不是Stream接口自身类型的方法,因此不在支持Stream的后续链式操作了

包括: count 和forEach

什么是链式调用 ?

就是 不用 ; 结束语句 继续在上一个方法基础上继续操作 形成一条链子一样

还可以称为管道 在现实中管道 一般就只有进和出

用代码表示就是创建Stream流对象 就是 结束分号 或者终结方法

而管道内部我们可以安装过滤网来过滤垃圾,或者处理这些垃圾

而Stream流,主要就是处理单例集合的比如Set List,毕竟管道只有一条,下面案例也是以这些集合为主

获取Stream流对象

只有获取流对象后才能使用Stream中的方法

  • 所有Collection接口包括子接口 都有默认的stream默认的方法获取流;
//把单列集合 转换为Stream流
        List<String> list=new ArrayList<>();
        Stream<String> stream = list.stream();
        Set<String> set=new HashSet<>();
        Stream<String> stream1 = set.stream();
        
        //把双列集合 转换为Stream流
        Map<String,String> map=new HashMap<>();

        //将所有的key 都转换为 Stream流
        Stream<String> stream2 = map.keySet().stream();
        //把所有的value 都转换为 Stream流
        Stream<String> stream3 = map.values().stream();

        //我们还可以 通过Entry 来转换为Stream流
        // 顺便 介绍下用法
        map.put("a","A");
        map.put("b","B");
        Stream<Map.Entry<String, String>> stream4 = map.entrySet().stream();
        stream4.forEach(entry-> System.out.println(entry.getKey()));
  • Stream接口的静态方法of 可以将数组转换为Stream 流
Integer[] array={1,2,3,4,5};
        Stream<Integer> array1 = Stream.of(array);
        Stream<String> array2 = Stream.of("a","b","c");

我们下面就来学习Stream流常用的方法:

下面列出来的都是后面案例中会用到的方法

Filter

filter 方法用于通过设置的条件过滤出元素
1.
ForEach

Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据
1.
map

map 方法用于映射每个元素到对应的结果
1.
limit

limit 方法用于获取指定数量的流
1.
sorted

sorted 方法用于对流进行排序
1.
Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素
1.
统计

一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上
1.
boxed

boxed的作用就是将int类型的stream转成了Integer类型的Stream

过滤

过滤指定元素
public static void main(String[] args) {
       List<String> list=new ArrayList<>(Arrays.asList("a","b","c"));
        List<String> b = list.stream()
                .filter(name -> !name.equals("b"))  //把b过滤掉 ,过滤的条件必须是Booleam
                .collect(Collectors.toList());//将过滤后的结果返回
    }
过滤后去重
Set<String> b1 = list.stream()
                .filter(name -> !name.equals("b"))  //把b过滤掉
                .collect(Collectors.toSet()); //转换成set去重
统计过滤后的元素数量
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();

处理集合每个值

List<User> list1 = list.stream().peek((x) -> {
                //满足条件修改
                if (x.getName().equals("xxx")) {
                    x.setName(x.getName() + "(?)");
                }

          }
        ).collect(Collectors.toList());

流式循环

forEach方法,用来遍历流中的数据 ,和我们使用的foreach 类似

forEach方法是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法

public static void main(String[] args) {

       List<String> list=new ArrayList<>(Arrays.asList("a","b","c"));
       list.stream().forEach(System.out::println);
    }

将集合转换Map

将user的id作为key,将user对象作为value 而 Function.identity()就是将user对象作为value的内置方法

userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));

也可以一个user的id对应一个user的name

userList.stream().collect(Collectors.toMap(User::getId, User::getName));

将集合某个成员属性转集合

List<String> idcards= users.stream().map(User::getIdcard).collect(Collectors.toList())

解释下一这行代码:

users:一个实体类的集合,类型为List<User>
User:实体类
getIdcard:实体类中的get方法,为获取User的idcard

将集合转换基本数据类型数组

目前只支持int double long 这3种转换

List<Integer> list=new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        int[] ints = list.stream().mapToInt(Integer::intValue).toArray();
        System.out.println(Arrays.toString(ints));

        List<Double> list1=new ArrayList<>();
        list1.add(1.1);
        list1.add(1.1);
        list1.add(1.1);

        double[] doubles = list1.stream().mapToDouble(Double::doubleValue).toArray();
        System.out.println(Arrays.toString(doubles));

        List<Long> list2=new ArrayList<>();
        list2.add(1L);
        list2.add(1L);
        list2.add(1L);
        long[] longs = list2.stream().mapToLong(Long::longValue).toArray();
        System.out.println(Arrays.toString(longs));

把基本数据类型转集合

//把int数据基础类型数组转集合
int[] nums = new int[]{2, 2, 1, 1, 1, 2, 2};
List<Integer> numsList = Arrays.stream(nums).boxed().collect(Collectors.toList());
//把double基础数据类型数组转集合
double[] nums = new double[]{2.1, 2.2, 1, 1, 1, 2, 2};
List<Double> collect = Arrays.stream(nums).boxed().collect(Collectors.toList());
System.out.println(collect);

把基础字符串数组转换集合字符串数组

//把字符串数组转换成int
        String[] arrs = {"huawei", "tencent", "huawei", "tencent", "ali"};
        List<String> arrsList = Arrays.stream(arrs).collect(Collectors.toList());

把字符串集合转换成指定集合类型

//把字符串集合转换成int
List<String> list=new ArrayList<>(Arrays.asList("1","2","3"));
List<Integer> collect = 			               list.stream().map(Integer::parseInt).collect(Collectors.toList());

数组字母变小写变大写

List<String> list= Arrays.asList("a", "b", "c", "d");

List<String> collect =list.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println(collect); //[A, B, C, D]

集合所有元素,按指定规则计算

计算每个元素n²
List<Integer> num = Arrays.asList(1,2,3,4,5);
List<Integer> collect1 = num.stream().map(n -> n * 2).collect(Collectors.toList());
System.out.println(collect1); //[2, 4, 6, 8, 10]
找偶数
List<Integer> list2 = Arrays.asList(1, 2, 4);
        List<Integer> list3 = list2.stream().
                filter(key -> key % 2 == 0).
                collect(Collectors.toList());
        System.out.println(list3);
找奇数
List<Integer> list2 = Arrays.asList(1, 2, 4);
        List<Integer> list3 = list2.stream().
                filter(key -> key % 2 != 0).
                collect(Collectors.toList());
        System.out.println(list3);

统计个数count

List<String> list=new ArrayList<>(Arrays.asList("1","2","3"));
        Stream<String> stream = list.stream();
        long count = stream.count();
        System.out.println(count);

和,最大,最小,平均值

基本数据类型集合的统计

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
 IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
 System.out.println("列表中最大的数 : " + stats.getMax());
 System.out.println("列表中最小的数 : " + stats.getMin());
 System.out.println("所有数之和 : " + stats.getSum());
 System.out.println("平均数 : " + stats.getAverage());

BigDecimal数据类型集合统计

List<BigDecimal> uList = new ArrayList<>();
        uList.add(new BigDecimal("10.11"));
        uList.add(new BigDecimal("12.11"));
        uList.add(new BigDecimal("18.11"));

        //和
        BigDecimal reduce = uList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println(reduce.setScale(2,BigDecimal.ROUND_HALF_UP));

        //最大
        Optional<BigDecimal> max = uList.stream().max((u1, u2) -> u1.compareTo(u2));
        System.out.println(max.get().setScale(2,BigDecimal.ROUND_HALF_UP));

        //最小
        Optional<BigDecimal> max = uList.stream().min((u1, u2) -> u1.compareTo(u2));
        System.out.println(max.get().setScale(2,BigDecimal.ROUND_HALF_UP));

        //平均数
BigDecimal avg = uList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(avg.divide(new BigDecimal(String.valueOf(uList.size())),2,BigDecimal.ROUND_HALF_UP));

对象数据类型集合的统计

案例对象

package com.jdbc.dome.entity;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class UserInfo {
    private int id;
    private String name;
    private String num;
    private  double price0;
    private BigDecimal price1;

    public UserInfo(int id, String name, String num, double price0, BigDecimal price1) {
        this.id = id;
        this.name = name;
        this.num = num;
        this.price0 = price0;
        this.price1 = price1;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNum() {
        return num;
    }

    public void setNum(String num) {
        this.num = num;
    }

    public double getPrice0() {
        return price0;
    }

    public void setPrice0(double price0) {
        this.price0 = price0;
    }

    public BigDecimal getPrice1() {
        return price1;
    }

    public void setPrice1(BigDecimal price1) {
        this.price1 = price1;
    }

    public static void main(String[] args) {

        List<UserInfo> uList = new ArrayList<>();
        UserInfo user1 = new UserInfo(1,"小白","15600000000",10,new BigDecimal(10.11));
        UserInfo user2 = new UserInfo(2,"小黑","15500000000",15,new BigDecimal(20.2));
        UserInfo user3 = new UserInfo(2,"小彩","15500000000",88,new BigDecimal(99.1));
        uList.add(user1);
        uList.add(user2);
        uList.add(user3);

    }
}
//和
Double d1 = uList.stream().mapToDouble(UserInfo::getPrice0).sum();
        System.out.println(d1);

//最大
Double d2 = uList.stream().mapToDouble(UserInfo::getPrice0).max().getAsDouble();
        System.out.println(d2);
//最小
Double d3 = uList.stream().mapToDouble(UserInfo::getPrice0).min().getAsDouble();
        System.out.println(d3);
//平均值
DecimalFormat decimalFormat=new DecimalFormat("0.00");
Double d4 = uList.stream().mapToDouble(UserInfo::getPrice0).average().getAsDouble();
System.out.println(decimalFormat.format(d4));
//和
BigDecimal add = uList.stream().map(UserInfo::getPrice1).reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(add.setScale(2,BigDecimal.ROUND_HALF_UP));

//最大
Optional<UserInfo> max = uList.stream().max((u1, u2) -> u1.getPrice1().compareTo(u2.getPrice1()));
System.out.println(max.get().getPrice1().setScale(2,BigDecimal.ROUND_HALF_UP));

//最小
Optional<UserInfo> max = uList.stream().min((u1, u2) -> u1.getPrice1().compareTo(u2.getPrice1()));
System.out.println(max.get().getPrice1().setScale(2,BigDecimal.ROUND_HALF_UP));

//平均值
BigDecimal avg = uList.stream().map(UserInfo::getPrice1).reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(avg.divide(new BigDecimal(String.valueOf(uList.size())),2,BigDecimal.ROUND_HALF_UP));

分页

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        int pageNo = 2;//获取第二页数据
        int pageSize = 3;//每页3条数据
        int skip = (pageNo - 1) * pageSize;//初始页从1开始,计算从第几条开始
        List<Integer> pageList = numbers.stream()
// .sorted(Comparator.comparingInt(Student::getTotalScore).reversed()) //分页前排序,或者其他处理
                .skip(skip)
                .limit(pageSize)
                .collect(Collectors.toList());

        System.out.println(pageList);  //[4, 5, 6]

合并多个Stream对象

concat 是Stream的静态方法 用于将二个Stream流拼接到一起 和 + 类似 但是前提必须将集合和数组转换为Stream对象才能拼接

public static void main(String[] args) {

        List<String> list = new ArrayList<>(Arrays.asList("1", "2", "3"));
        List<String> list1 = new ArrayList<>(Arrays.asList("4", "5", "6"));
        Stream<String> concat = Stream.concat(list.stream(), list1.stream());
        concat.forEach(System.out::print);

    }

123456

对于以上方法还不太看的懂的 我们这里弄一个小的练习 来将上面的方法组合使用

现在有两个集合存储队伍当中多个成员姓名

第一个队伍只要名称为3个字的成员姓名
1.
第一个队伍只要前三个人
1.
第二个队伍只要姓名为张的成员
1.
第二个队伍不要前两个人

  • 将两个队伍合并为一个队伍
  • 根据姓名创建Person对象
  • 打印整个队伍的person对象信息
public class Person {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石中天");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");

        Stream<String> oneStream = one.stream()
                .filter(name -> name.length() == 3)
                .limit(3);

        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯xxx");
        two.add("张天爱");
        two.add("张二狗");
        Stream<String> twoStream = two.stream().
                filter(name -> name.startsWith("张"))
                .skip(2);

        Stream<String> concat = Stream.concat(oneStream,twoStream);
       concat.map(Person::new).forEach(p-> System.out.println(p.getName()));

集合元素排序

基础集合

List<Integer> list = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

        //升序
        List<Integer> collect1 = list.stream().sorted().collect(Collectors.toList());
        System.out.println(collect1);
         //降序
        List<Integer> collect2 = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        System.out.println(collect2);
List<BigDecimal> uList = new ArrayList<>();
        uList.add(new BigDecimal("17.11"));
        uList.add(new BigDecimal("12.11"));
        uList.add(new BigDecimal("18.11"));
        uList.add(new BigDecimal("13.12"));
        //升序
        List<BigDecimal> collect1 = uList.stream().sorted().collect(Collectors.toList());
        System.out.println(collect1);
         //降序
        List<BigDecimal> collect2 = uList.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        System.out.println(collect2);

对象集合排序

//按年龄升序排序
List<StudentInfo> studentsSortName = studentList.stream().sorted(Comparator.comparing(StudentInfo::getAge)).collect(Collectors.toList());

//按年龄降序排序
List<StudentInfo> studentsSortName = studentList.stream().sorted(Comparator.comparing(StudentInfo::getAge).reversed()).collect(Collectors.toList());

//使用年龄进行降序排序,年龄相同再使用身高升序排序
List<StudentInfo> studentsSortName = studentList.stream()
        .sorted(Comparator.comparing(StudentInfo::getAge)
         .reversed().thenComparing(StudentInfo::getHeight))
        .collect(Collectors.toList());

集合分组

User user1 = new User(1,"张三","小学");
        User user2 = new User(2,"李四","小学");
        User user3 = new User(3,"王五","初中");
        User user4 = new User(4,"马六","高中");

        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
// 将类型去重后最为map的key ,而同一个类型的对象会被放在同一个key的List里作为map的value
        Map<String,List<User>> userGroupMap = list.stream()
                .collect(Collectors.groupingBy(User::getType, LinkedHashMap::new, Collectors.toList()));

集合分组排序

User user1 = new User(1,"张三","小学",14);
        User user2 = new User(2,"李四","小学",15);
        User user3 = new User(3,"王五","初中",18);
        User user4 = new User(4,"马六","高中",24);

        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);      

		//按照年级进行排序,然后安装类型进行分组
        Map<String, List<User>> sortedDatas = list.stream()
                .sorted(Comparator.comparing(User::getAge, Comparator.nullsLast(Comparator.reverseOrder())))
                .collect(Collectors.groupingBy(User::getType, LinkedHashMap::new, Collectors.toList()));

去重

//对于string列表去重
List<String> stringList = list.stream()
						.distinct().collect(Collectors.toList());

//对于实体类的去重 必须实现hashcode()和equals()方法。
List<User> userList = list.stream()
						.distinct().collect(Collectors.toList());

//根据 List 中 Object 某个属性去重(姓名去重)
 List<User> userList = list.stream()
         .collect(
                 Collectors.collectingAndThen(
                         Collectors.toCollection(
                                 ()-> new TreeSet<>(
                                         Comparator.comparing(User::getName))), ArrayList::new));

//根据List 中 Object 多个属性去重(姓名,年龄去重)
List<User> studentList=list.stream()
        .collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(
                                () -> new TreeSet<>(
                         Comparator.comparing(o -> o.getName() + ";" + o.getAge()))), ArrayList::new));

反转

List<Integer> list = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> collect = Stream.iterate(list.size() - 1, n -> n - 1).limit(list.size()).map(n -> list.get(n)).collect(Collectors.toList());
System.out.println(collect); //[5, 3, 7, 3, 2, 2, 3]

多线程并行流

只需要在流的后面加上stream().parallel() 就能将顺序处理,变为多线程并发处理了,这可以很大程度简化我们使用并发操作,注意线程安全问题.

List<Integer> nums = Arrays.asList(1,2,3,4,5);
        nums.stream().parallel().forEach(System.out::println);
	   //缩写
        nums.parallelStream().forEach(num->System.out.println(num));

        //案例将所有值进行n²运算
        ArrayList<Integer> collect = nums.parallelStream()
                .map(n -> n * 2)
                .collect(Collectors.toCollection(ArrayList::new));

以指定符号分割然后字符串拼接

把所有集合内学生名字进行拼接,并以 ‘,’ 分割

String str = list.stream()
        .map(Student::getName)
        .collect(Collectors.joining(","));

System.out.println(str);

相关文章