java基础之接口基础

java基础之接口基础

一、lambda函数式表达

首先理解一个概念:

  • 函数式编程思想
  1. 在数学中,函数是有输入量,输出量的一套计算方案。就是“拿数据做操作”。

  2. 在面向对象的思想中强调“必须通过对象的形式来做事情

  3. 函数式思想则是尽量忽略面向对象的复杂语法:“强调的是做什么,而不是以什么形式去做

    而我们学的Lambda就是体现这种函数式思想的。

    注意:千万记住这几句话,真的很重要。下面所有操作全都围绕这几句话展开的。

举个例子:

要求:需要启动一个线程,输出一句话,多线程程序启动了

  1. (常规类):按照正常的步骤,我们需要先建一个类,这个类是Runnable的实现类并重写其run()方法,然后是在测试类里面去调用该类对象,把其作为参数构建Thread对象,然后使用Theard的start() 方法启动线程。`

    public class RunnableImpl implements Runnable{
        @Override
        public void run() {
            System.out.println("多线程已启动");
        }
    }
    ------------------------
    //测试类
    public static void main(String[] args) {
         //常规类方法       
      RunnableImpl runnable = new RunnableImpl();
            new  Thread(runnable).start();
    	}
    }
    
  2. (匿名内部类)在创建Thread对象的时候,在其参数位置直接new一个匿名内部类,然后实现在测试类直接重写run()方法,并调用Thread对象的strat() 方法,实现启动多线程。

//匿名内部类
new  Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("多线程已启动");
    }
}).start();
  1. (Lambda表达式)在匿名内部类的基础上,我们使用函数式思想,只在乎做的是什么,发现是创建的这个内部类进行了一句:输出一句字符串 ;

    所以我们把这个操作简化一下,

    参数:所以用()括起来,有多个参数可以用 , 隔开。

    执行了什么:我们用 ->表示。

    执行的东西:利用{ } 括起来。

    总结一下:Lambda三要素:形参(),箭头 ->,代码块 { } .

//lambda表达式

/*   new Thread(()->{
    System.out.println("多线程已启动");
        }).start();*/

  /*简化,简化前提:
   1.参数:有参数时,可把参数类型省略,只留下参数名;但是省略的时候要全部参数的类型名都要省略
   2.如果参数只有一个时,可以把()也省略掉。
   2.代码块:当代码块只有一句话的时候,可以把代码块的{}和;省略。
   3.当代码块种有return,且只有一句话,要把return也省略掉
  */
	//省略后
   new Thread(()-> System.out.println("多线程已启动")).start()

Lambda方式虽然使用起来十分的方便,但是这种方便的东西肯定是有一个前提的:

/*Lambda使用前提:
1.当使用到接口,且接口有且仅有一个抽象方法的时候。
2.有上下文环境,能理解其调用的是什么方法,调用的参数是什么。
	根据局部变量的赋值得知Lambda对应的接口:
	Runnable r = ()->System.out.println("Lambda表达式");
	根据调用方法的参数得知Lambda对应的接口:
	new Thread(()->System.out.println("Lambda表达式").start();
*/

省略return的代码:

public class AddDemo1 {
    public static void main(String[] args) {
        //具体类
        AddInterface a1 = new AddImpliment();
        a1.add(10, 20);
        //当一个方法,要用到接口对象来完成一个操作,就可以利用匿名内部类
        useadd(new AddInterface() {
            @Override
            public int add(int x, int y) {
                return x+y;
            }
        });
        //当该方法调用的接口是有且仅有一个抽象方法时,可以使用lambda函数化表达
        useadd((int x,int  y)->{
            return x+y;
        });
        //形参可以省略类型名,方法体只有一条语句可以直接省略大括号和分号,
        // 而且具有return的语句。return也可以省略
        // 简化后:
        useadd((x,y)->x+y);
        //lambda是相当于代码简化的匿名内部类
        //根据函数化思想只展现出谁干了什么。而不在乎是用什么形式去做的。
    }
    private  static  void useadd(AddInterface i ){
        i.add( 10,20);
    }
}

二、lambda函数式表达与匿名内部类的区别

1.用法区别

  • lambda表达作用对象:

    • 接口类(有且仅有一个抽象方法)
  • 匿名内部类作用对象:

    • 可以是具体类(可以具有多种方法)

    • 可以是接口类(可以具有多种方法)

    • 可以是抽象类(可以具有多种方法)

2.底层原理

  • lambda表达:
    • 对应的字节码文件会在运行时动态生成。
    • 不会在产生其他的字节码文件。
  • 匿名内部类:
    • 在运用匿名内部类的方法执行的程序,会在磁盘产生两个字节码文件
      • 第一个文件是原来的文件的字节码文件;设原类的字节码文件为 LambadDemo.class
      • 第二个文件则是匿名内部类的字节码文件;则为 LambdaDemo$1.class

3.接口的组成更新

  • 接口的组成
    • 常量
      • Public static final
    • 抽象方法
      • public abstract

jdk8以后,更新了接口的组成更新,加入了

  • 默认方法
    • pubilc default
  • 静态方法
    • pubilc static

jdk9以后,加入了

  • 私有方法
    • private

3.1接口的默认方法

默认方法的出现,是为了增强程序的扩展性。

举个例子:

若一个写好的接口类,我想扩展一个新的方法,那么我如果直接写一个抽象方法进去,所以以匿名内部类方法调用该接口的类,都需要重写新写入的方法。

要解决这个方法可以在一个新的接口种写入该方法,然后去继承这个要扩展的接口,但是这种方法会让代码维护量变得十分庞大。

所以java8提出了默认方法,只需要在原来的接口中,把想扩展的方法用关键字default修饰。就可解决该问题。


接口种默认方法的定义格式:

  • 格式:pubilc default 返回值类型 方法名(参数列表){ }
  • 范例 pubilc default void add( int x,int y){ }
  • 注意事项:
    • 默认方法不是抽象方法,可以有具体方法体
    • 不强制被重写。但是可以重写,重写的时候把default关键字去掉
    • 在接口内定义时,public可以省略,但是default不能省略
3.2接口的静态方法

看见静态方法一定要想起一句话:静态方法是类的方法,不是成员方法。

接口的静态方法其实和具体类的成员的静态方法是一样的格式的,而且也是有方法体的。

接口钟静态方法的定义格式:

  • 格式:public static 返回值类型 方法名(参数列表){ }
  • 范例: public static void show( ) { }
  • 注意事项:

接口的静态方法是只允许接口名调用的,而不是给接口实现类对象名或者是接口的实现类名调用。

因为若一个实现类他继承自多个接口,则无法区别这个静态方法到底是来自谁的。

类似这种格式调用 Thread.start();					 //伊呦
	而不能是 runnable.start();					   //哒咩
	或者是Runnable r =new Runnble(); r.strat();	//哒咩呦

在接口中定义时,public是可以省略的,但是static是不能省略的。

3.3接口的私有方法

java9中,新增了带方法体的私有方法;

由于java8的默认方法和静态方法引进,导致一个问题:就是如果一个接口中具有两个默认方法或者静态方法,并且这两个方法具有一个共性方法的时候。

我们其实按习惯的编程思想,是会把这个共性方法封装到一个私有(封装为私有是为了不给外部利用)的成员方法中从而减少相同代码块带来的冗余的。

于是在java9就新增了这样一种只给内部使用的方法——私有方法。

私有方法分为普通的私有方法和静态的私有方法。

格式:

  • 格式1:private 返回值类型 方法名 (参数列表){ }
  • 范例 : private void show() { }
  • 格式2:private static 返回值类型 方法名 (参数列表){ }
  • 范例 : private static void show() { }
  • 注意:
    • 静态的私有方法能同时被默认和静态方法调用,
    • 普通的私有方法是动态的,不能被静态方法调用。

4.方法引用

方法引用其实就是lambda函数式思想的延续,

lambda的原理就是:

在原有的情况下,为了突显**“什么东西干了什么”**,

lambda就在引用这个方法,并且省略了执行方式。

但是其实这个方法是具体存在的。

于是就有一个问题,既然是有方法,并且我们是面向对象的编程。

那我们在遇到相同的情况,直接把这个原有的方法拿过来就很快了鸭。于是就搞了个方法引用符。

引用运算符::用这个鬼东西,表示哪个东西去引用了哪个方法的过程。

::这个东西在的表达式就称为 方法引用。


在代码中举个例子,

lambda表达式:usePrintable(s->System.out.println(s));

lambda的处理是,把参数s传给System.out这个对象的println方法去处理。

方法引用:usePrintable(System.out::println)

所以方法引用这里,直接把这个参数s,传给了println这个方法去执行。而这个方法是system.out的。就把这个方法的对象拿过来,放在前面。

可以看出,方法引用其实是Lambda的孪生兄弟。


上面的只是一个例子推出两者间的关系,下面是两者具体还要什么关系可以互相转化表达。

Lambda表达式支持的方法引用:

常见的引用方式:

  • 引用类方法
  • 引用对象的实例方法
  • 引用类的实例方法
  • 引用构造器
4.1引用类方法

引用类方法,其实就是引用类的静态方法

  • 格式:类名::静态方法

  • 范例:Integer::parseInt

    • 注释:Interger类中有的一个parseInt方法

      public static int parseInt(String s)将此String型转化为int型数据
      在这里插入图片描述
      代码展示:

//测试类
public class ConverterDemo {
    public static void main(String[] args) {
        //Lambda表达式
        useConverter(s -> Integer.parseInt(s));
        //方法引用
        useConverter(Integer::parseInt);
        //可以看出,Lambda表达式被方法引用代替时,是把参数全部传输到类的静态方法里。
    }
    private static void useConverter(converter c){
        c.convert("666");
    }
}

//接口类
public interface converter {
    int convert(String s);
}
4.2引用对象的实例方法

引用对象的实例方法,其实就是引用类的成员方法

  • 格式:对象::成员方法
  • 范例:“HelloWorld”::toUpperCase
    • 注释:String类中方法:public String toUpperCase () 将此String所有字符转化为大写。

练习:

在这里插入图片描述
代码:

//自定义类
public class PrintString {
    public void  printupper( String s){
        String result = s.toUpperCase();
        System.out.println(result);
    }
}

//接口类
public interface Printer {
    void printUpperCase(String s );
}

//测试类
public class PrinterDemo {
    public static void main(String[] args) {
        //

        //lambda
        usePrinter(s-> System.out.println(s.toUpperCase()));
        
        //方法引用(printString对象的成员方法)
        PrintString printString = new PrintString();//把对象引入
        usePrinter(printString::printupper);
        //lambda表达式被方法引用替代时,参数全部传给成员方法。
    }
    private  static void  usePrinter(Printer p ){
        p.printUpperCase("HelloWorld");
    }
}

4.3引用类的实例方法

引用类的实例方法,其实就是引用类中的成员方法

  • 格式 :类名::成员方法
  • 范例:String::toUpperCase

其实刚刚那个引用对象的实例方法,也可以用作引用类的实例方法的例子。

看代码:

package com.lys.heima.javaSEjichu.Lambda;

import com.sun.org.apache.bcel.internal.generic.NEW;

public class PrinterDemo {
    public static void main(String[] args) {
        //

        //lambda
        usePrinter(s-> System.out.println(s.toUpperCase()));

        //方法引用(String类的方法)引用类的实例方法
        usePrinter(String::toUpperCase);

        //方法引用(printString对象的成员方法)引用对象的实例方法
        PrintString printString = new PrintString();//把对象引入
        usePrinter(printString::printupper);
        //lambda表达式被方法引用替代时,参数全部传给成员方法。
    }
    private  static void  usePrinter(Printer p ){
        p.printUpperCase("HelloWorld");
    }
}
4.3引用构造器

引用构造器,其实就是引用构造方法

  • 格式:类名::new
  • 范例:Student::new

在这里插入图片描述
代码:

//自定义学生类
public class Student {
    private int age ;
    private String name;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
//接口类
public interface StudentBuilder {
    Student build(String name,int age);
}

测试类
public class StudentDemo {
    public static void main(String[] args) {
        //lambda
        /* useStudentBuilder((name,age)->{
        return new Student("王五",15);
        });*/

        //简化
        useStudentBuilder((name,age)-> new Student("王五",15));

        //方法引用(引用构造器)
        useStudentBuilder(Student::new);
        //可以看出当lambda被方法引用代替,是把数据直接传输给student类的构造器的。

    }
    private  static  void  useStudentBuilder(StudentBuilder S){
        S.build("张三",18);
    }
}

三、函数式接口

1.函数式接口概述

函数式接口:有且只有一个抽象方法。

java中的函数式编程体现的就是lambda表达式,所以函数式接口就是可以适用于lambda使用的接口,只要确保接口中有且仅有一个抽象方法,java中的lambda才能顺利进行推导。

如何检测一个接口是不是函数式接口呢?

  • @FunctionalInterface
  • 把这个注解定义在接口的定义上方:如果接口是函数式接口,编译通过,否则会报错

对于我们自己定义来说,这个@FunctionalInterface注解是可选的,起的是一个提醒功能,即使不写,符合函数式接口条件,它依然是函数式接口是不变的。但是,开发的时候还是建议写上。

//这个是API中的Runnable类

@FunctionalInterface		//这个就是函数式接口的注解
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();

2.函数式接口作为方法的参数

如果方法的参数是一个函数式接口,可以使用lambda表达式作为参数传递

  • StartThread(() ->System.out.println(Thread.currentThread().getName()+“线程启动了”));
    • StartThread(Runnable r )这个方法就是参数为Runnable。

3.函数式接口作为方法的返回值

如果方法的返回值是一个函数式接口,可以使用Lambda表达式作为结果返回

  • private static Comparator getCompartor(){

    return (s1,s2 )->s1.length - s2.length

    }

    • Comparator是集合里面的比较器,当初还是通过重写匿名内部类在测试类里定义集合元素排序的。

4.常用函数式接口

在java 8在java.util.function包下预定义了大量的函数式接口

常见的4个:

  1. Supplier接口
  2. Consumer接口
  3. Perdicate接口
  4. Function接口
4.1Supplier接口

Supplier:包含一个无参的方法

  • T get0:获得结果
  • 该访法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
  • Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
package com.lys.heima.javaSEjichu.Lambda.Supplier;

import java.util.function.Supplier;
//Supplier测试类
public class SupplierDemo1 {
    public static void main(String[] args) {
        int x = getInteger(()->10 );
        String s = getString(()->"String");
        System.out.println(s+"\t"+x);

        //返回最大值
        int[] arr = {0,5,6,22,3,444,888};
        int max1  = getMax(arr,()-> {
                    int max =  arr[0];
                    for (int i = 0; i < arr.length; i++) {

                        if (max <arr[i]){
                            max=arr[i];
                        }
                    }
                    return max;
                 }
                );
        System.out.println(max1);

    }
    //定义一个方法,返回一个整数数据
    private static Integer getInteger(Supplier<Integer> sup){
        return sup.get();
    }
    //定义一个方法,返回一个字符串数据
    private static  String getString(Supplier<String> sup){
        return sup.get();
    }
    //定义一个方法,返回一个数组中的最大值
    private static int getMax(int[] x ,Supplier<Integer> sup){
        return sup.get();
    }
}
/*结果:
 *String	10
 *888
 * */
4.2Consumer接口

Consumer:包含两个方法

  • void accept(T t):对给定的参数执行此操作
  • default ConsumerandThen(Comsumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作。
  • Consumer Consumer接口被称为消费型接口。它消费的数据的数据类型由泛型指定。
//消费型接口
import java.util.function.Consumer;

public class consumerDemo1 {
    public static void main(String[] args) {
        //函数式编程思想:什么东西干了什么;
        consumerStr("陈奕迅",(s1)-> System.out.println(s1));

        consumerTwoStr("陈奕迅",           //第一个参数
                (s2) -> System.out.println(s2), //第二个参数
                (s2)-> System.out.println(s2+"我又来啦")   //第三个参数
        );

        String[] Array = {"林青霞,30", "张曼玉,35","王祖贤,33"};
        consumerPrint(Array,
                (name)-> System.out.print(name.split(",")[0])
                ,
                (age)-> System.out.println( Integer.parseInt(age.split(",")[1]))
        );

    }
    //定义一个方法,消费一个字符串数据
    private static void  consumerStr(String s1, Consumer<String> C){
        C.accept(s1);
    }
    //定义一个方法,用不同的方式消费同一个字符串数据两次
    private  static  void consumerTwoStr(String s2,Consumer<String> c1,Consumer<String> c2){
        /*
        c1.accept(s2);
        c2.accept(s2);
        */
        c1.andThen(c2).accept(s2);//尽量使用通过方法封装的连用两个参数的方法。
    }
    //根据格式“姓名:xx,年龄:xx”的格式把信息从String[]中打印出来。
    //● String[] Array = {"林青霞,30", "张曼玉,35","王祖贤,33"};
    //要求:
    //把打印姓名的动作作为第一个Consumer接口的Lambda实例
    //把打印年龄的动作作为第: -个Consumer接口的L .ambda实例
    //将两个Consurer接口按照顺序组合到一起使用
    private static void consumerPrint(String[] s, Consumer<String> c1, Consumer<String> c2) {
        for (String ss : s){
            c1.andThen(c2).accept(ss);
        }
    }
}
/*
结果:
陈奕迅
陈奕迅
陈奕迅我又来啦
林青霞30
张曼玉35
王祖贤33

进程已结束,退出代码为 0

*/
4.3Predicate接口

Predicate:常用的四个方法

  • boolean test(T t):对给定的参数进行判断(判断逻辑由L ambda表达式实现),返回一个布尔值
  • default Predicate negate0:返回-个逻辑的否定,对应逻辑非
  • default Predicate and(Predicate other):返回-一个组合判断,对应短路与
  • default Predicate or(Predicate other):返回一个组合判断,对应短路或
  • Predicate接口通常用于判断参数是否满足指定的条件

代码:

//
import java.util.ArrayList;
import java.util.function.Predicate;

public class PredicateDemo {
    public static void main(String[] args) {
        boolean a = CheckS1("我在学java", (s1) -> s1.length() > 8);
        System.out.println(a);
        System.out.println("------------------------------");
        boolean b = checkS2("我在一边听歌,一边写这个代码",
                (s2) -> s2.length() > 8
                ,
                (s2) -> s2.length() == 8 ? true : false//顺便复习一下三元运算符
        );
        System.out.println(b);

        System.out.println("------------------------------");
        String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};
        ArrayList<String> alist = new ArrayList();
        for (String str:strArray) {
            boolean b2 =checkS3(str,
                    (name) -> name.split(",")[0].length() > 2
                    ,
                    (age) -> Integer.parseInt(age.split(",")[1]) > 33
            );
            if (b2){
                alist.add(str);
            }
        }
        for (String s4 : alist){
            System.out.println(s4);
        }


    }

    //判断给定的字符串是否满足条件
    private static boolean CheckS1(String s1, Predicate<String> p1) {
        //   return p1.test(s1);  //原式   false
        return p1.negate().test(s1);//非  ture
    }

    //同一个字符串给出两个不同的判断条件,最后把这两个结果进行逻辑与运算作为结果返回;
    private static boolean checkS2(String s2, Predicate<String> p1, Predicate<String> p2) {
  /*      boolean b1 = p1.test(s2);
        boolean b2 = p2.test(s2);
        boolean b = b1&&b2;
        return b;*/
        //利用封装好的方法
        return p1.and(p2).test(s2); //返回逻辑与
        //对应的返回逻辑或
        //return p1.or(p2).test(s2);
    }

    //StringOstrArray= {"林青霞,30", "柳岩,34", "张曼玉,35",“貂蝉,31", "王祖贤,33"};
    //字符串数组中有多条信息,请通过Predicate接口的拼装将符合要 求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
    //同时满足如下要求:姓名长大于2;年龄大于33
    private static boolean checkS3(String s3, Predicate<String> p1, Predicate<String> p2) {
        return p1.and(p2).test(s3);
    }
}
/*
结果:
true
------------------------------
false
------------------------------
张曼玉,35

进程已结束,退出代码为 0
* */
4.4Function接口

Function<T,R>:常用的两个方法

  • R apply(Tt): 将此函数应用于给定的参数
  • default Function andihen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果

Function<T,R>接口通常用于对参数进行处理,转换处理逻辑由Lambda表达式实现),然后返回一个新的值

//Function<T,R>接口通常用于对参数进行处理,转换处理逻辑由Lambda表达式实现),然后返回一个新的值
import java.util.function.Function;

public class FuntionDemo1 {
    public static void main(String[] args) {

        convertInt("100", (s1)-> Integer.parseInt(s1));
        System.out.println("------------------------------------");
        convertAddStr(100, (s1)-> Integer.parseInt(s1));
        System.out.println("------------------------------------");
        StrAddStr("100",
               // (s)->Integer.parseInt(s)
                Integer::parseInt
                ,
                (i)->String.valueOf(i+200)
        );
        System.out.println("------------------------------------");
        function1("林青霞,30",
        (s)->Integer.parseInt(s.split(",")[1])
        ,
         (s)->s+70
        );


    }
    //定义一个方法,把一个字符串转换int类型,在控制台输出
    private static void convertInt(String s1, Function<String,Integer> f1){
        /* System.out.println(  f1.apply(s1)); */
        Integer int1= f1.apply(s1);
        System.out.println(int1+"我是int类型的");
    }
    //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convertAddStr(int x ,Function<String,Integer> f2 ){
        String value = String.valueOf(x + 100);
        /*System.out.println(f2.apply(value));*/
        Integer int2 = f2.apply(value);
        System.out.println(int2+"我是int类型的");
    }
    //定义-个方法,把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static  void StrAddStr (String s , Function<String,Integer> f3,Function<Integer,String> f4  ){
        /*System.out.println(f3.andThen(f4).apply(s));*/
        String string = f3.andThen(f4).apply(s);
        System.out.println(string+"我是字符串");
    }
  /*        Strings = "林青霞,30";
            请按照我指定的要求进行操作:
                1:将字符串截取得到数字年龄部分
                2:将上一步的年龄字符串转换成为int类型的数据
                3:将上一步的int数据加70,得到一个int结果,在控制台输出
            请通过Function接口来实现函数拼接*/
    private static void function1(String s , Function<String,Integer> f5,Function<Integer,Integer> f6  ){
        //可以拆分为三个部分,也可以一个fybction就完成,看你的实现怎么编写而已。
        Integer int3 = f5.andThen(f6).apply(s);
        System.out.println(int3+"我是int类型的");
    }

}
/*
结果:
100我是int类型的
------------------------------------
200我是int类型的
------------------------------------
300我是字符串
------------------------------------
100我是int类型的

进程已结束,退出代码为 0

*/

四、Stream流

1.体验Stream

要求:

  1. 把姓“张”的提取出来到一个ArrayList集合。
  2. 再把这个ArrayList中长度大于等于3的元素提取出来。放到一个新的ArrayList集合。
  3. 遍历最后的集合。
//体验Stream流
import java.util.ArrayList;
import java.util.Arrays;

/*要求:
        1. 把姓“张”的提取出来到一个ArrayList集合。
        2. 再把这个ArrayList中长度大于等于3的元素提取出来。放到一个新的ArrayList集合。
        3. 遍历最后的集合。*/
public class StreamDemo1 {
    public static void main(String[] args) {
        String[] str= {"张三","张三丰","张无忌","王五","李四"};
        ArrayList<String> strarray1 = new ArrayList();
       for (String s : str) {
           if (s.startsWith("张")){
           strarray1.add(s);
        }
       }
        ArrayList<String> strarray2 = new ArrayList();
       for (String s :strarray1){
           if (s.length()>=3){
               strarray2.add(s);
           }
       }
       for (String s : strarray2){
           System.out.println(s);
       }
        System.out.println("-----------------------------");
       //可以发现要完成一次筛选就要3-5行的代码,显得十分的复杂麻烦。使用引出了一个stream流的概念
        Arrays.stream(str).filter((s)->s.startsWith("张")).filter(s -> s.length()>=3).forEach( System.out::println);
    }                                                                              //forEach(s->System.out.println(s))
}
/*
结果:
张三丰
张无忌
-----------------------------
张三丰
张无忌

进程已结束,退出代码为 0

*/

2.stream流的生成方式

从刚刚给出的实例可以推导出:

Stream流的使用

  • 生成流
    • 通过数据源(集合,数组等)生成流
      list.stream0
  • 中间操作
    • 一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤映射,然后返回一个新的流,
      交给下一个操作使用
      filter0
  • 终结操作
    • 一个流只能有一个终结操作, 当这个操作执行后,流就被使用"光"了,无法再被操作。所以这必定是流的最后一个操作
      forEach()

3.Stream流的常见生成方式

Stream流的常见生成方式

  • Collection体系的集合可以使用默认方法stream0生成流
    defaultStream stream0
    • 范例:list.stream();
  • Map体 系的集合间接的生成流
    • 范例:
      • ketSet().stream();//键集合的stream流
      • values().stream();//值集合的stream流
      • entrySet().stream();//键值对集合的stream流
  • 数组可以通过Stream接口的静态方法f(T… values)生成流
    • 范例:Stream().of(“a”,“b”,“c”)
package com.lys.heima.javaSEjichu.Lambda.Stream;
//Stream生成方式
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamDemo2 {
    public static void main(String[] args) {
        //collection集合
        ArrayList<String> list = new ArrayList();
        Stream liststream = list.stream();
        //map集合
        Map<String,String> map = new HashMap<>();
        Stream<String> key = map.keySet().stream();
        Stream<String> values = map.values().stream();
        Stream<Map.Entry<String, String>> entry = map.entrySet().stream();
        //数组
        String[] str ={"111","222","333"};
        Stream<String> str1 = Stream.of(str);
        //Stream<String> str1=Stream.of("111","222","333");
        Stream<Integer> integerStream = Stream.of(11,22,33);
    }
}

4.Stream流的常见中间操作方法

  • Stream filter(Predicate predicate):用于对流中的数据进行过滤。
    • 底层依赖于 Predicate接口中的方法 boolean test(T t) :对给定的参数进行判断,返回一个布尔值。
  • Stream limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据。
  • Stream skip(long n):跳过指定参数个数的数据,返回由该流的剩元素组成的流。
  • static Stream concat(Stream a, Stream b):合并a和b两个流为一个流
  • Stream distinct():返回由该流的不同元素(根据Objectequals(Object)组成的流
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Stream;

//Stream的中间操作方法1
public class StreamDemo3 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList();
        list.add("张家辉");
        list.add("张学友");
        list.add("贱辉");
        list.add("大口辉");
        list.add("化骨龙");
        list.add("狄龙·辉");

        //前面推导Stream的时候用过的筛选方法
        list.stream().filter((s)->s.startsWith("张")).filter(s -> s.length()>=3).forEach( System.out::println);
        System.out.println("1-----------------------");
        //1.取前3个数据在控制台输出
        list.stream().limit(3).forEach(System.out::println);
        System.out.println("2-----------------------");
        //2.跳过3个元素,把剩下的元素在控制台输出
        list.stream().skip(3).forEach(System.out::println);
        System.out.println("3-----------------------");
        //3.跳过2个元素,把剩下的元素中前2个在控制台输出
        list.stream().skip(2).limit(2).forEach(System.out::println);

        //需求4:取前4个数据组成-一个流
        Stream<String> limit = list.stream().limit(4);
        //需求5:跳过2个数据组成-一个流
        Stream<String> skip = list.stream().skip(2);
        System.out.println("6-----------------------");
        //需求6:合并需求1和需求2得到的流,并把结果在控制台输出
        //Stream.concat(limit,skip).forEach(System.out::println);
        System.out.println("7-----------------------");
        //需求7:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
        Stream.concat(limit,skip).distinct().forEach(System.out::println);

    }
}
/*
结果:
张家辉
张学友
1-----------------------
张家辉
张学友
贱辉
2-----------------------
大口辉
化骨龙
狄龙·辉
3-----------------------
贱辉
大口辉
6-----------------------
7-----------------------
张家辉
张学友
贱辉
大口辉
化骨龙
狄龙·辉

进程已结束,退出代码为 0
*/

以为完了?还有呢!!!

  • Stream sorted0:返回由此流的元素组成的流,根据自然顺序排序
  • Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
  • Stream map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流.
    • 依赖于Function接口中的方法上 R apply(T t)
  • IntStream mapTolnt(ToIntFunction mapper):返回一个ntStream其中包含将给定函数应用于此流的元素的结果
    • IntStream: 表示原始int流
    • 底层依赖于TolntFunction接口中的方法 int applyAsInt(T value)
Stream的中间操作方法2
import java.util.ArrayList;
import java.util.stream.IntStream;


public class StreamDemo4 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList();
        list.add("hello");
        list.add("java");
        list.add("c++");
        list.add("phyton");
        list.add("c#");
        list.add("php");

        //需求1。按照字母顺序把数据在控制台输出
        list.stream().sorted().forEach(System.out::println);
        System.out.println("2----------------------");
        //需求2:按照字符串长度把数据在控制台输出
        list.stream().sorted((s1,s2)->s1.length()-s2.length()).forEach(System.out::println);
        System.out.println("3-----------------------");
        ArrayList<String> intarr = new ArrayList();
        intarr.add("10");
        intarr.add("20");
        intarr.add("30");
        intarr.add("40");
        intarr.add("50");

        //需求3: 将集合中的字符串数据转换为整数之后在控制台输出
        // 利用<R> Stream<R> map(Function mapper)方法
        intarr.stream().map((s)->Integer.parseInt(s)).forEach(System.out::println);
        System.out.println("4-----------------------");
        //需求4: 将集合中的字符串数据转换为整数之后在控制台输出
        //利用IntStream mapTolnt(ToIntFunction mapper)方法
        intarr.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
        //利用IntStream流特有方法,sum()求和
        int sum = intarr.stream().mapToInt(Integer::parseInt).sum();
        System.out.println(sum);
        //总结两者差异:
        //map方法返回的是根据你在function接口操作后的数据类型进行判断类型的。没有具体的一个类型锁定。
        //但是maptoInt方法是要求返回的流的类型必须是IntStream类型的。
    }

}

4.Stream流的常见终结方法

Stream流的常见终结操作方法

  • void forEach(Consumer action):对此流的每个元执行操作

    • 依赖于Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
  • long count(): 返回此流中的元素数

package com.lys.heima.javaSEjichu.Lambda.Stream;

import java.util.ArrayList;

//常见Stream终结方式
public class StreamDemo5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList();
        list.add("张家辉");
        list.add("张学友");
        list.add("贱辉");
        list.add("大口辉");
        list.add("化骨龙");
        list.add("狄龙·辉");

        //需求1:把集合中的元素在控制台输出
        list.stream().forEach(System.out::println);
        //需求2:统计集合中有几个以张开头的元素,并把统计结果在控制台输出
        System.out.println("2------------------------------");
        long count = list.stream().filter(s -> s.startsWith("张")).count();
        System.out.println("张姓人数有:"+count);

    }
}
/*
结果:
张家辉
张学友
贱辉
大口辉
化骨龙
狄龙·辉
2------------------------------
张姓人数有:2

进程已结束,退出代码为 0

*/

5.Stream流的练习

现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作

  • 男演员只要名字为3个字的前三人
  • 女演员只要姓林的, 粗不要第一个
  • 把过滤后的男演员姓名和女演员姓名合并到起
  • 把上一步操作后的元素作为构造方法的参 数创建演员对象遍历数据
    • 演员类Actor已经提供,里面有一个成员变量, 一个带参构造方法,以吸成员变量对应的get/set方法
import java.util.ArrayList;
import java.util.stream.Stream;

/*Stream流的练习:
* 男演员只要名字为3个字的前三人
* 女演员只要姓林的, 且不要第一个
* 把过滤后的男演员姓名和女演员姓名合并到起-
* 把上一步操作后的元素作为构造方法的参 数创建演员对象遍历数据
* 演员类Actor已经提供,里面有一个成员变量, 一个带参构造方法,以吸成员变量对应的get/set方法*/
public class StreamDemo6 {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> manList = new ArrayList<String>();
        manList.add("周润发");
        manList.add( "成龙");
        manList.add("刘德华");
        manList.add("吴京");
        manList.add("周星驰");
        manList.add("李连杰");
        ArrayList<String> womanList = new ArrayList<String>();
        womanList.add("林心如");
        womanList.add("张曼玉");
        womanList.add("林青霞");
        womanList.add("柳岩");
        womanList.add("林志玲");
        womanList.add("王祖贤");
//        * 男演员只要名字为3个字的前三人
        Stream<String> man = manList.stream().filter(s -> s.length() == 3).limit(3);
//        * 女演员只要姓林的, 且不要第一个
        Stream<String> woman = womanList.stream().filter(s -> s.startsWith("林")).skip(1);
//        * 把过滤后的男演员姓名和女演员姓名合并到一起
        Stream<String> concat = Stream.concat(man, woman);
//        * 把上一步操作后的元素作为构造方法的参 数创建演员对象遍历数据
//        * 演员类Actor已经提供,里面有一个成员变量, 一个带参构造方法,以吸成员变量对应的get/set方法*/
       /* Stream<Actor> actorStream = concat.map(Actor::new);
        actorStream.forEach(s-> System.out.print(s.getName()+" "));*/
        concat.map(Actor::new).forEach(s->System.out.println(s.getName()));
//
    }
}
/*结果:
周润发
刘德华
周星驰
林青霞
林志玲

进程已结束,退出代码为 0
*/

6.Stream流的收集操作

对数据使用Stream流的方式操作完毕后,我想把流中的数据收集到集合中,该怎么办呢?
Stream流的收集方法

  • R collect(Collector collector)
  • 但是这个收集方法的参数是 -个Collector接口

Colletor是一个接口,无法提供方法,但是提供了它的实现工具类Collectors。

工具类Collectors提供了具体的收集方式

  • public static Collector toList0:把元素收集到List集合中
  • public static Collector toSet():把元素收集到Set集合中
  • public static Collector toMap(Function keyMapper,FunctionvalueMapper):把元素收集到Map集合中
import javax.xml.transform.Source;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

//对Stream的结果收集
public class StreamDemo7 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张曼玉");
        list.add("林青霞");
        list.add("柳岩");
        list.add("王祖贤");
        //需求1:得到名字为3个字的流
        Stream<String> name = list.stream().filter(s -> s.length() == 3);
        //需求2:把使用Stream流操作完毕的数据收集到list集合中并遍历
        List<String> newlist = name.collect(Collectors.toList());
        newlist.forEach(System.out::println);
        System.out.println("-----------------------------");
        //set集合
        Set<Integer> set = new HashSet<Integer>();
        set.add(10);
        set.add(20);
        set.add(30);
        set.add(33);
        set.add(35);
        //需求3:得到年龄大于25的流
        Stream<Integer> age = set.stream().filter(s -> s > 25);
        //需求4:把使用Stream流操作完毕的数据收集到Set集合并遍历
        age.collect(Collectors.toSet()).forEach(System.out::println);
        System.out.println("-----------------------------");

        //定义一个字符串数组,每个字符串数据有姓名数据和年龄数据组合而成
        String[] strings = {"林青霞,30", "张曼玉,35", "王祖贤,33","柳岩,25"};

        //需求5:得到字符串中年龄数据大于28的流
        Stream<String> agestream = Arrays.stream(strings).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
        //需求6:把使用Stream流操作完毕的数据收集到Map集合并遍历,字符串中的姓名做键,年龄做值
        Map<String, String> map = agestream.collect(Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
       //三种遍历方式
        //1.键值对流
        map.entrySet().forEach(System.out::println);
        System.out.println("-----------------------------");
        //2.键流
        map.keySet().forEach(key-> System.out.println(key+","+map.get(key)));
        System.out.println("-----------------------------");
        //普通foreach循环
        Set<String> keySet = map.keySet();
        for (String S:keySet){
              String V=map.get(S);
            System.out.println(S+","+V);
        }
        //懂得流的使用,比起普通方式做操作,实在简单太多了。同样遍历一个集合。用Stream方式只需要1句,常规操作要4句。
    }
}

t.add(35);
//需求3:得到年龄大于25的流
Stream age = set.stream().filter(s -> s > 25);
//需求4:把使用Stream流操作完毕的数据收集到Set集合并遍历
age.collect(Collectors.toSet()).forEach(System.out::println);
System.out.println("-----------------------------");

     //定义一个字符串数组,每个字符串数据有姓名数据和年龄数据组合而成
     String[] strings = {"林青霞,30", "张曼玉,35", "王祖贤,33","柳岩,25"};

     //需求5:得到字符串中年龄数据大于28的流
     Stream<String> agestream = Arrays.stream(strings).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
     //需求6:把使用Stream流操作完毕的数据收集到Map集合并遍历,字符串中的姓名做键,年龄做值
     Map<String, String> map = agestream.collect(Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
    //三种遍历方式
     //1.键值对流
     map.entrySet().forEach(System.out::println);
     System.out.println("-----------------------------");
     //2.键流
     map.keySet().forEach(key-> System.out.println(key+","+map.get(key)));
     System.out.println("-----------------------------");
     //普通foreach循环
     Set<String> keySet = map.keySet();
     for (String S:keySet){
           String V=map.get(S);
         System.out.println(S+","+V);
     }
     //懂得流的使用,比起普通方式做操作,实在简单太多了。同样遍历一个集合。用Stream方式只需要1句,常规操作要4句。
    }
 }
 /*
结果:
张曼玉
林青霞
王祖贤
-----------------------------
33
35
30
-----------------------------
林青霞=30
王祖贤=33
张曼玉=35
-----------------------------
林青霞,30
王祖贤,33
张曼玉,35
-----------------------------
林青霞,30
王祖贤,33
张曼玉,35

进程已结束,退出代码为 0

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YeungSLee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值