Heim  >  Artikel  >  Java  >  Beispielanalyse für die funktionale Java-Programmierung

Beispielanalyse für die funktionale Java-Programmierung

WBOY
WBOYnach vorne
2023-04-28 09:28:151369Durchsuche

Die meisten Leute verstehen die Wissenspunkte dieses Artikels „Beispielanalyse der Java-Funktionsprogrammierung“ nicht, daher hat der Herausgeber den folgenden Inhalt für Sie zusammengefasst. Der Inhalt ist detailliert, die Schritte sind klar und er hat einen gewissen Referenzwert. Ich hoffe, dass Sie es alle lesen können, nachdem Sie diesen Artikel gelesen haben. Werfen wir einen Blick auf diesen Artikel „Analyse der funktionalen Java-Programmierung“.

1. Lambda-Ausdruck

1.1 Überblick über funktionale Programmierideen

In der Mathematik ist eine Funktion eine Reihe von Berechnungsschemata mit Eingabe- und Ausgabegrößen, also „Arbeiten mit Daten“

Der Schwerpunkt liegt auf objektorientiertem Denken „Dinge müssen in Form von Objekten erledigt werden“

Funktionales Denken betont, wie wichtig es ist, komplexe objektorientierte Aussagen zu ignorieren: „Der Schwerpunkt liegt darauf, was zu tun ist, nicht in welcher Form es zu tun ist“

Und der Lambda-Ausdruck, den wir wollen Lernen ist die Verkörperung des funktionalen Denkens.

1.2 Erleben Sie den Lambda-Ausdruck Klasse MyRunnable-Schnittstelle, überschreiben Sie die Ausführungsmethode

Erstellen Sie ein Objekt der MyRunnable-Klasse. Erstellen Sie ein Thread-Klassenobjekt und übergeben Sie das MyRunnable-Objekt als Konstruktionsparameter. Starten Sie den Thread

Methode 2:

Verbessern Sie auf der Grundlage von Methode 1 und verwenden Sie anonyme innere Klassen

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("多线程程序启动了");
    }
}
  • Methode 3:

  • Verbesserung von Lambda-Ausdrücken:

            MyRunnable myRunnable = new MyRunnable();
            Thread thread = new Thread(myRunnable);
            thread.start();
  • 1.3 Das Standardformat von Lambda-Ausdrücken
  • in anonymen inneren Klassen Klassen Codeanalyse zum Umschreiben der run()-Methode:
  • Die formalen Parameter der Methode sind leer, was darauf hinweist, dass beim Aufruf der Methode keine Parameter übergeben werden müssen

Der Rückgabewerttyp der Methode ist ungültig, was darauf hinweist, dass nein Es werden Ergebnisse aus der Methodenausführung zurückgegeben.

Der Inhalt im Methodenkörper ist das, was wir speziell tun möchten Es, das als formaler Parameter der Methode betrachtet werden kann, ist leer

->: Verwenden Sie Pfeile, um auf Dinge zu zeigen, die später erledigt werden müssen

{}: Enthält einen Code, den wir Rufen Sie einen Codeblock auf, der als Inhalt im Methodenkörper angezeigt werden kann

  • Format:

    (formale Parameter) ->{Codeblock}

  • Formale Parameter:
  • Wenn mehrere Parameter vorhanden sind, trennen Sie diese durch Kommas; wenn keine Parameter vorhanden sind, lassen Sie sie einfach leer
->:

besteht im Englischen aus einem Unterstrich und einem Größer-als-Zeichen und ist in einer festen Schreibweise geschrieben. Stellt eine Zeigeaktion dar

  • Codeblock:

    Es ist das, was wir konkret tun müssen, nämlich den Inhalt des Methodenkörpers, den wir zuvor geschrieben haben

  • 1.4 Übung von Lambda-Ausdrücken
  • Voraussetzungen für die Verwendung von Lambda-Ausdrücken

  • Es gibt eine Schnittstelle
  • Es gibt und gibt nur eine abstrakte Methode in der Schnittstelle

Übung 1:

    Definieren Sie eine Schnittstelle (Eatable), die eine abstrakte Methode definiert: void eat( );
  • Definieren Sie eine Testklasse (EatableDemo) und stellen Sie

    zwei Methoden in der Testklasse bereit:
  • Eine Methode ist:
  • useEatable(Eatable e)

    Eine Methode ist die Hauptmethode , in der Hauptmethode Rufen Sie die useEatable-Methode in auf
  • Definieren Sie eine Schnittstelle:
      new Thread(new Runnable() {
           @Override
            public void run() {
                System.out.println("多线程程序启动了");
            }
        }).start();

Methode 1:

Traditionelle Schnittstellenimplementierungsklasse

        new Thread(() -> {
            System.out.println("多线程程序启动了");
        }).start();
      new Thread(new Runnable() {
           @Override
            public void run() {
                System.out.println("多线程程序启动了");
            }
        }).start();
  • Methode 2:

    Anonyme innere Klasse

            new Thread(() -> {
                System.out.println("多线程程序启动了");
            }).start();
  • Methode 3 :

    Lambda-Ausdruck

    public interface Eatable {
        void eat();
    }
  • Die laufenden Ergebnisse sind die gleichen

Übung 2:

  • Definieren Sie eine Schnittstelle (Flyable), die eine abstrakte Methode definiert: void fiy(String s);

  • Definieren Sie a Testklasse (FlyableDemo), in der Testklasse werden zwei Methoden bereitgestellt Hauptmethode

  • public class EatableImpl implements Eatable{
        @Override
        public void eat() {
            System.out.println("一日三餐,必不可少");
        }
    }
    public class EatableDemo{
        public static void main(String[] args) {
            Eatable eatable = new EatableImpl();
            eatable.eat();
        }
    
        private static void useEatable(Eatable eatable){
            eatable.eat();
        }
    }
    • Übung 3:

    • Definieren Sie eine Schnittstelle (Addable), die eine abstrakte Methode definiert: int add(int x, int y);
    • Definieren Sie eine Testklasse (AddableDemo ) und stellt zwei Methoden in der Testklasse bereit

Eine Methode ist:

useAddable(Addable a)

Eine Methode ist die Hauptmethode, in der die useAddable-Methode aufgerufen wird

public class EatableDemo{
    public static void main(String[] args) {
        useEatable(new Eatable() {
            @Override
            public void eat() {
                System.out.println("一日三餐,必不可少");
            }
        });
    }
    private static void useEatable(Eatable eatable){
        eatable.eat();
    }
}
rrree1.5 Auslassungsmuster des Lambda-Ausdrucks

Auslassungsregeln:

Der Parametertyp kann weggelassen werden. Wenn es mehrere Parameter gibt, können Sie nicht nur einen weglassen
  • 如果参数有且仅有一个,那么小括号可以省略

  • 如果代码块的语句只有一条,可以省略大括号和分号,甚至时return

public class LambdaDemo5 {
    public static void main(String[] args) {
        //参数类型可以省略
        useAddable((x, y) -> {
            return x + y;
        });
        System.out.println("------------------------");
        //如果只有一个参数,小括号也可以省略
        useFlyable(s -> {
            System.out.println(s);
        });
        System.out.println("------------------------");
        //如果代码块的语句只有一条,可以省略大括号和分号(有return时要把return也去掉)
        useFlyable(s ->
            System.out.println(s)
        );
        useAddable((x,y)->x+y);
    }
    private static void useFlyable(Flyable flyable) {
        flyable.fly("风和日丽,晴空万里");
    }
    private static void useAddable(Addable addable) {
        int sum = addable.add(10, 20);
        System.out.println(sum);
    }
}

接口类参考1.4

1.6 Lambda表达式的注意事项

注意事项:

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象的方法

  • 必须有上下文环境,才能推导出Lambda对应的接口

    • 根据局部变量的赋值得知Lambda对应的接口:Runnable r =() ->System.out.println(“Lambda表达式”);

    • 根据调用方法的参数得知Lambda对应的接口:new Thread(()->System.out.println(“Lambda表达式”)).start();

public interface Inter {
    void show();
}
public class LambdaDemo6 {
    public static void main(String[] args) {
        useInter(()->
                System.out.println("Lambda表达式")
        );
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        }).start();
        Runnable r = () -> System.out.println("Lambda表达式");
        new Thread(r).start();

        new Thread(()->
            System.out.println("Lambda表达式")
        ).start();
    }
    private static void useInter(Inter inter){
        inter.show();
    }
}

1.7 Lambda表达式和匿名内部类的区别

所需类型不同:

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类

  • Lambda表达式:只能是接口

使用限制不同:

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类

  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

实现原理不同:

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件

  • Lambda表达式:编译之后,没有一个单独的.class字节码文件,对应的字节码会在运行的时候动态生成

二、接口组成更新

1.1 接口组成更新概述

接口的组成:

  • 常量:public static final

  • 抽象方法:public abstract

  • 默认方法(Java 8)

  • 静态方法(Java 8)

  • 私有方法 (Java 8)

1.2 接口中默认方法

接口中默认方法得定义格式:

  • 格式:public default 返回值类型 方法名(参数列表){}

  • 范例:public default void show3(){}

接口中默认方法的注意事项:

  • 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字

  • public可以省略,default不能重写

public interface MyInterface {
    void show1();

    void show2();

    default void show3(){
        System.out.println("show3");
    }
}
public class MyInterfaceImplOne implements MyInterface{
    @Override
    public void show1() {
        System.out.println("One show1");
    }

    @Override
    public void show2() {
        System.out.println("One show2");
    }
}
public class MyInterfaceImplTwo implements MyInterface{
    @Override
    public void show1() {
        System.out.println("Two show1");
    }

    @Override
    public void show2() {
        System.out.println("Two show2");
    }
}
public class InterfaceDemo {
    public static void main(String[] args) {
        MyInterface myInterface = new MyInterfaceImplOne();
        myInterface.show1();
        myInterface.show2();
        myInterface.show3();
        System.out.println("------------------");
        MyInterface myInterface2 = new MyInterfaceImplTwo();
        myInterface2.show1();
        myInterface2.show2();
        myInterface2.show3();
    }
}

运行结果:

One show1
One show2
show3
------------------
Two show1
Two show2
show3

1.3 接口中静态方法

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

  • 格式:public static 返回值类型 方法名(参数列表){ }

  • 范例:public static void show(){ }

接口中静态方法的注意事项:

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

  • public可以省略,static不能省略

public interface Inter {
    void show();
    default void method() {
        System.out.println("Inter 中的默认方法执行了");
    }
    public static void test(){
        System.out.println("Inter 中的静态方法执行了");
    }
}
public class InterImpl implements Inter{
    @Override
    public void show() {
        System.out.println("show方法执行了");
    }
}
public class InterDemo {                                           
    public static void main(String[] args) {                       
        Inter inter = new InterImpl();                             
        inter.show();                                             
        inter.method();                                           
        Inter.test();                                             
    }                                                             
}

1.4 接口中私有方法

Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法时不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性。

接口中私有方法的定义格式:

  • 格式1:private 返回值类型方法名(参数列表){ }

  • 范例1:private void show(){ }

  • 格式2:private static 返回值类型 方法名(参数列表){ }

  • 范例2:private static void method(){ }

接口中私有方法的注意事项:

  • 默认方法可以调用私有的静态方法和非静态方法

  • 静态方法只能调用私有的静态方法

public interface Inter {
    default void show1(){
        System.out.println("show1开始执行");
        method();
        System.out.println("show1结束执行");
    }
    default void show2(){
        System.out.println("show2开始执行");
        method();
        System.out.println("show2结束执行");
    }
    static void method1(){
        System.out.println("method1开始执行");
        method();
        System.out.println("method1结束执行");
    }
    static void method2(){
        System.out.println("method2开始执行");
        method();
        System.out.println("method2结束执行");
    }
    static void method(){
        System.out.println("初级工程师");
        System.out.println("中级工程师");
        System.out.println("高级工程师");
    }
}
public class InterImpl implements Inter{
}
public class InterDemo {
    public static void main(String[] args) {
        Inter inter = new InterImpl();
        inter.show1();
        System.out.println("------------------------");
        inter.show2();
        System.out.println("------------------------");
        Inter.method1();
        System.out.println("------------------------");
        Inter.method2();
    }
}

三、方法引用

1.1 体验方法引用

通过方法引用来使用已经存在的方案

public interface Printable {
    void printString(String s);
}
public class PrintableDemo {
    public static void main(String[] args) {
        usePrintable(s->
            System.out.println(s)
        );

        usePrintable(System.out::println);
    }
    private static void usePrintable(Printable p){
        p.printString("hello world");
    }
}

1.2 方法引用符

  • ::该符号为引用运算符,而它所在表达式被称为方法引用符

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

分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理

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

分析:直接使用System.out中的println方法来取代Lambda,代码更加的简洁

推导与省略:

  • 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导

  • 如果使用方法引用,也同要可以根据上下文进行推导

  • 方法引用是Lambda的孪生兄弟

public interface Printable {
    void printInt(int i);
}
public class PrintableDemo2 {
    public static void main(String[] args) {
        usePrintable(i -> System.out.println(i));

        usePrintable(System.out::println);
    }
    private static void usePrintable(Printable printable){
        printable.printInt(1);
    }
}

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

常见的引用方式:

  • 引用类方法

  • 引用对象的实例方法

  • 引用类的实例方法

  • 引用构造器

1.4 引用类方法

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

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

  • 范例:Integer::parseInt

    • Integer类的方法:public static int parsenInt(String s),将此String转换为int类型数据

    • Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数

public interface Converter {
    int convert(String s);
}
public class ConverterDemo {
    public static void main(String[] args) {
        useConverter(s -> Integer.parseInt(s));
        useConverter(Integer::parseInt);
    }
    private static void useConverter(Converter c) {
        int number = c.convert("666");
        System.out.println(number);
    }
}

1.5 引用对象的实例方法

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

  • 格式:对象::成员方法

  • 范例:“HelloWorld"::toUpperCase

  • String 类中的方法:public String toUpperCase()将此String所有字符转为大写

  • Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数

public interface Printer {
    void printUpperCase(String s);
}
public class PrintString {
    public void printUpper(String s){
        String result = s.toUpperCase();
        System.out.println(result);
    }
}
public class PrinterDemo {
    public static void main(String[] args) {
        usePrinter(s -> System.out.println(s.toUpperCase()));

        PrintString printString = new PrintString();
        usePrinter(printString::printUpper);
    }
    private static void usePrinter(Printer printer) {
        printer.printUpperCase("HelloWorld");
    }
}

1.6 引用类的实例方法

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

  • 格式:类名::成员方法

  • 范例:String::substring

  • String类中的方法:public String subString(int beginIndex,int endIndex)从beginIndex开始到endIndex结束,截取字符串。返回一个子串,字串的长度为endIndex-beginIndex

  • Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数

public interface MyString {
    String mySubString(String s, int x, int y);
}
public class MyStringDemo {
    public static void main(String[] args) {
        useMyString((s, x, y) -> s.substring(x, y));

        useMyString(String::substring);
    }
    private static void useMyString(MyString myString){
        String s = myString.mySubString("HelloWorld", 5, 10);
        System.out.println(s);
    }
}

1.7 引用构造器

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

  • 格式:类名::new

  • 范例:Student::new

  • Lambda表达式被构造器代替的时候,它的形式参数全部传递给构造器作为参数

public class Student {
    private String name;
    private int age;
    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() {
    }
    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) {
        useStudentBuilder((name, age) -> new Student(name,age));

        useStudentBuilder(Student::new);
    }

    private static void useStudentBuilder(StudentBuilder studentBuilder){
        Student student = studentBuilder.build("xuanxuan", 22);
        System.out.println(student.getName()+","+student.getAge());
    }
}

四、函数式接口

1.1 函数接口概述

函数式接口:有且仅有一个抽象方法的接口

Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以使用于Lambda使用的接口

只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导

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

  • @FunctionalInterface

  • 放在接口定义的上方:如果接口是函数接口,编译通过;如果不是,编译失败

注意:

我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我们不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上注解。

@FunctionalInterface
public interface MyInterface {
    void show();
}
public class MyInterfaceDemo {
    public static void main(String[] args) {
        MyInterface myInterface = ()-> System.out.println("函数式接口");
        myInterface.show();
    }
}

1.2 函数式接口作为方法的参数

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

startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
public class RunnableDemo {
    public static void main(String[] args) {
        startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "线程启动了");
            }
        });

        startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));

        startThread(()->{
            System.out.println(Thread.currentThread().getName() + "线程启动了");
        });
    }
    private static void startThread(Runnable runnable) {
        new Thread(runnable).start();
    }
}

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

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

private static Comparator<String> getComparator() {
        return (s1,s2) -> s1.length() - s2.length();
    }
public class ComparatorDemo {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<String>();

        arrayList.add("ccc");
        arrayList.add("aa");
        arrayList.add("dddd");
        arrayList.add("b");
        System.out.println("排序前" + arrayList);
        Collections.sort(arrayList);
        System.out.println("排序后" + arrayList);
        Collections.sort(arrayList, getComparator());
        System.out.println("使用定义比较器排序方法后:" + arrayList);
    }

    private static Comparator<String> getComparator() {
//        return new Comparator<String>() {
//            @Override
//            public int compare(String s1, String s2) {
//                return s1.length() - s2.length();
//            }
//        };
        return (s1,s2) -> s1.length() - s2.length();
    }
}

1.4 常用的函数式接口

Java 8 在java.util.function包下预定了大量的函数式接口供我们使用,常用如下:

  • Supplier接口

  • Consumer接口

  • Predicate接口

  • Function接口

1.5 Supplier接口

Supplier接口

  • T get():获得结果

  • 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据

  • Supplier 接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用

public class SupplierDemo {
    public static void main(String[] args) {
        String s = getString(() -> "xuanxuan");
        System.out.println(s);

        Integer i = getInteger(() -> 666);
        System.out.println(i);
    }

    public static String getString(Supplier<String> supplier) {
        return supplier.get();
    }
    public static Integer getInteger(Supplier<Integer> supplier) {
        return supplier.get();
    }
}

练习:获取最大值

public class SupplierDemo {
    public static void main(String[] args) {
        int[] arr = new int[]{17, 28, 49, 21, 32, 66};
        int maxNumber = getMax(() -> {
            int max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (max < arr[i]) {
                    max = arr[i];
                }
            }
            return max;
        });
        System.out.println("数组中的最大值是:" + maxNumber);
    }

    private static int getMax(Supplier<Integer> supplier) {
        return supplier.get();
    }
}

1.6 Consumer接口

Consumer:包含两个方法

  • void accept(T t):对给定的参数执行此操作

  • default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作

  • Consumer 接口也被称为消费型接口,它消费的数据类型由泛型指定

public class ConsumerDemo {
    public static void main(String[] args) {
        operatorString("abc", s -> System.out.println(s));
        operatorString("abc", System.out::println);
        operatorString("abc", s -> System.out.println(new StringBuilder(s).reverse().toString()));
        System.out.println("----------------------------------");
        operatorString("abc", s -> System.out.println(s), s -> System.out.println(new StringBuilder(s).reverse().toString()));
    }

    private static void operatorString(String name, Consumer<String> consumer) {
        consumer.accept(name);
    }

    private static void operatorString(String name, Consumer<String> consumer1, Consumer<String> consumer2) {
//        consumer1.accept(name);
//        consumer2.accept(name);
        consumer1.andThen(consumer2).accept(name);
    }
}

练习:

字符串数组中又多条信息,按照:“姓名:name,年龄:age"的格式将信息打印出来

public class ConsumerDemo {
    public static void main(String[] args) {
        String[] arr = new String[]{"abc,30", "cbd,35", "dna,33"};
        printInfo(arr, s -> System.out.print("姓名:" + s.split(",")[0] + ","), s -> System.out.println("年龄:" + Integer.parseInt(s.split(",")[1])));
    }

    private static void printInfo(String[] arr, Consumer<String> consumer1, Consumer<String> consumer2) {
        for (String s : arr) {
            consumer1.andThen(consumer2).accept(s);
        }
    }
}

1.7 Predicate接口

常用方法:

Beispielanalyse für die funktionale Java-Programmierung

练习:判断给定的字符串是否满足要求

public class PredicateDemo {
    public static void main(String[] args) {
        boolean b1 = checkString("hello", s -> s.length() > 5);
        System.out.println(b1);

        boolean b2 = checkString("helloworld", s -> s.length() > 8);
        System.out.println(b2);

        boolean b3 = checkString("hello", s -> s.length() > 5, s -> s.length() > 8);
        System.out.println(b3);

        boolean b4 = checkString("helloworld", s -> s.length() > 5, s -> s.length() > 8);
        System.out.println(b4);
    }

    private static boolean checkString(String s, Predicate<String> predicate) {
        return predicate.test(s);
    }

    private static boolean checkString(String s, Predicate<String> predicate, Predicate<String> predicate2) {
//        return predicate.and(predicate2).test(s);
        return predicate.or(predicate2).test(s);
    }
}

练习2:

  • String[] strArray ={“孙悟空,30”,“唐僧,36”,“沙僧,34”,“猪八戒,32”,“白骨精,5000”}

  • 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayLitst中,并遍历ArrayLitst集合

  • 同时满足如下要求:name长度大于2,age大于33

public class PredicateDemo3 {
    public static void main(String[] args) {
        String[] strArray = new String[]{"孙悟空,30", "唐僧,36", "沙僧,34", "猪八戒,32", "白骨精,5000"};
        ArrayList<String> arrayList = myFilter(strArray, s -> s.split(",")[0].length() > 2, s -> Integer.parseInt(s.split(",")[1]) > 33);
        System.out.println("name长度大于2,age大于33有:");
        for (String s : arrayList) {
            System.out.print("name:" + s.split(",")[0] + ",");
            System.out.println("age:" + Integer.parseInt(s.split(",")[1]));
        }
    }

    private static ArrayList<String> myFilter(String[] strArray, Predicate<String> predicate1, Predicate<String> predicate2) {
        ArrayList<String> arrayList = new ArrayList<String>();
        for (String s : strArray) {
            if (predicate1.and(predicate2).test(s)) {
                arrayList.add(s);
            }
        }
        return arrayList;
    }
}

1.8 Function接口

Function两个常用方法:

Beispielanalyse für die funktionale Java-Programmierung

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

练习:

public class FunctionDemo {
    public static void main(String[] args) {
        convert("100", s -> Integer.parseInt(s));
        convert("100", Integer::parseInt);

        convert(100, i -> String.valueOf(100 + i));

        convert("100", s -> Integer.parseInt(s), i -> String.valueOf(i + 566));

    }
    //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(String s, Function<String, Integer> function) {
        Integer i = function.apply(s);
        System.out.println(i);
    }
    //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(int i, Function<Integer, String> function) {
        String s = function.apply(i);
        System.out.println(s);
    }
    //定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(String s, Function<String, Integer> function1, Function<Integer, String> function2) {
        String ss = function2.apply(function1.apply(s));
        System.out.println(ss);
    }
}

练习2:提取String中的年龄加70岁,并以int型输出

public class FunctionDemo {
    public static void main(String[] args) {
        String s = "孙悟空,30";
        convert(s, s1 -> s1.split(",")[1], s1 -> Integer.parseInt(s1) + 70);
    }

    private static void convert(String s, Function<String, String> function1, Function<String, Integer> function2) {
        Integer i = function2.apply(function1.apply(s));
        System.out.println(i);
    }
}

五、Stream流

1.1 体验Stream流

需求:按照下面的要求完成集合的创建和遍历

  • 创建一个集合,存储多个字符串元素

  • 把集合中所有以“张”开头的元素存储到一个新的集合再

  • 把长度为3的元素存储到一个新集合

  • 最后遍历上一步得到的集合

使用Stream流的方式完成过滤操作:

  • 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:生成流、过滤姓氏、过滤长度为3、逐一打印

  • Stream流把真正的函数式编程风格引入到java中

list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
public class StreamDemo {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");

        ArrayList zhangList = new ArrayList();
        for (String s : list) {
            if (s.startsWith("张")) {
                zhangList.add(s);
            }
        }
        ArrayList treeList = new ArrayList();
        for (String s : zhangList) {
            if (s.length() == 3) {
                treeList.add(s);
            }
        }
        for (String s : treeList) {
            System.out.println(s);
        }
        System.out.println("-------------------------------");
        //Stream流改进
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
    }
}

1.2 Stream流的生成方式

Stream流的使用

生成流:通过数据源(集合、数组等)生成流

list.stream();

中间操作:一个流后面可以跟随零个或者多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用

filter()

终结操作:一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作

forEach()

Stream流的常见生成方式

Collection体系的集合可以使用默认方法stream()生成流

default Stream<E> stream()
  • Map体系的集合间接的生成流

  • 数组可以通过Stream接口的静态方法of(T…values)生成流

public class StreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<String>();
        Stream<String> setStream = set.stream();

        Map<String, Integer> map = new HashMap<String, Integer>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<Integer> valueStream = map.values().stream();
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        String[] strArray = {"hello", "world", "java"};
        Stream<String> strArrayStream = Stream.of(strArray);
        Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
        Stream<Integer> strArrayStream3 = Stream.of(10, 20, 30);
    }
}

1.3 Stream流的常见中间操作方法

  • Stream filter(Predicate predicate):用于对流中的数据进行过滤

  • Predicate接口中的方法:boolean test(T t):对给定的参数进行判断,返回一个布尔值

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");
        list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
        System.out.println("----------------------");
        list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
        System.out.println("----------------------");
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
    }
}
  • Stream limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据

  • Stream skip(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");
        //取前三个数据在控制台输出
        list.stream().limit(3).forEach(System.out::println);
        System.out.println("-----------------------------");
        //跳过2个元素,把剩下的元素在控制台上输出
        list.stream().skip(2).forEach(System.out::println);
        System.out.println("-----------------------------");
        //跳过2个元素并将剩下元素的前两个元素在控制台上输出
        list.stream().skip(2).limit(2).forEach(System.out::println);
    }
}
  • Stream Stream concat(Stream a,Stream b):合并a和b两个流为一个流

  • Stream distinct:返回由该流的不同元素(根据Objectequals(Object))组成的流

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");
        //需求1:取前4个数据组成一个流
        Stream<String> limitStream = list.stream().limit(4);
        //需求2:跳过2个数据组成一个流
        Stream<String> skipStream = list.stream().skip(2);
        //需求3:合并需求1和需求2得到的流,并把结果在控制台输出
//        Stream.concat(limitStream,skipStream).forEach(System.out::println);
        //需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
        Stream.concat(limitStream,skipStream).distinct().forEach(System.out::println);
    }
}
  • Stream sorted():返回由此流的元素组成的流,根据自然顺序排序

  • Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("zhangfei");
        list.add("zhangsanfeng");
        list.add("zhangsan");
        list.add("lisi");
        list.add("sunwukong");
        list.add("zhangyifei");

        //需求1:按照字母顺序把数据在控制台输出
        list.stream().sorted().forEach(System.out::println);
        //需求2:按照字符串长度把数据在控制台输出
        list.stream().sorted((s1, s2) -> {
            int num = s1.length() - s2.length();
            int num2 = num == 0 ? s1.compareTo(s2) : num;
            return num2;
        }).forEach(System.out::println);
    }
}
  • Stream map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流(Function接口中的方法 R apply(T t))

  • IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("10");
        list.add("20");
        list.add("30");
        list.add("40");
        list.add("50");

//        list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
        list.stream().map(Integer::parseInt).forEach(System.out::println);

        list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);

        int result = list.stream().mapToInt(Integer::parseInt).sum();
        System.out.println(result);
    }
}

1.4 Stream流的常见终结操作方法

  • void forEach(Consumer action):对此流的每个元素执行操作(Consumer接口中的方法 void accept(T t):对给定的参数执行此操作)

  • long count():返回此流中的元素数

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");

        //需求1:把集合中的元素在控制台输出
        list.stream().forEach(System.out::println);

        //需求2:统计集合中有几个姓张的元素并在控制台输出
        list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
    }
}

1.5 Stream流的练习

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

  • 男演员只要名字为3个字的前三人

  • 女演员只要姓林的,并且不要第一个

  • 把过滤后的男演员姓名和女演员姓名合并到一起

  • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据(演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法)

public class StreamDemo {
    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> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);

        //女演员只要姓林的,并且不要第一个
        Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);

        //把过滤后的男演员姓名和女演员姓名合并到一起
        Stream<String> stream = Stream.concat(manStream, womanStream);

        //把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
        stream.map(Actor::new).forEach(p -> System.out.println(p.getName()));
        System.out.println("------------------------------------");
        //改进
        Stream.concat(manList.stream().filter(s -> s.length() == 3).limit(3), womanList.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).forEach(p -> System.out.println(p.getName()));
    }
}

1.6 Stream流的收集操作

对数据使用Stream流的方式操作完毕后,如何把流中的数据收集到集合中?

Stream流的手机方法

  • R collect(Collector collector)

  • 但是这个收集方法的参数是一个Collector接口

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

  • public static Collector toList():把元素收到List集合中

  • public static Collector toSet():把元素收集到Set集合中

  • public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");

        //需求1:得到名字为3个字的流
        Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
        //需求2:把使用Stream流操作完毕的数据收集到List集合中并遍历
        List<String> collect = listStream.collect(Collectors.toList());
        for (String s : collect) {
            System.out.println(s);
        }

        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> integerStream = set.stream().filter(age -> age > 25);

        //需求4:把使用Stream流操作完毕的数据收集到Set集合中并遍历
        Set<Integer> collect2 = integerStream.collect(Collectors.toSet());
        for (Integer i : collect2) {
            System.out.println(i);
        }

        String[] strArray = {"张飞,28", "张三丰,33", "张三,26", "李四,44"};

        //需求5:得到字符串年龄中数据大于28的流
        Stream<String> stringStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);

        //需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串的姓名作为键,年龄作为值
        Map<String, Integer> map = stringStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            Integer value = map.get(key);
            System.out.println(key + "," + value);
        }
    }
}

Das obige ist der detaillierte Inhalt vonBeispielanalyse für die funktionale Java-Programmierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen