24 Java的Stream流、方法引用

Stream流、方法引用

一、Stream流

1. 两种获取Stream流的方式

  • java.util.stream.Stream<T>Java 8新加入的最常用的流接口。(这并不是一个函数式接口)
  • 获取流的方式:
    • 所有的Collection集合都可以通过stream默认方法获取流。
      • default Stream<E> stream()
    • Stream接口的静态方法of可以获取数组对应的流。
      • static <T> Stream<T> of(T... values)
      • 参数是一个可变参数,那么就可以传递一个数组。
import java.util.*;
import java.util.stream.Stream;

public class Test {
    public static void main(String[] args) {
        //把Collection集合转换为流
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

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

        Map<String,String> map = new HashMap<>();
        //获取键
        Set<String> keySet = map.keySet();
        Stream<String> stream3 = keySet.stream();
        //获取值
        Collection<String> values = map.values();
        Stream<String> stream4 = values.stream();
        //获取键与值的映射
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream5 = entries.stream();

        //把数组转换为流
        Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5);

        String[] arr = {"a","bb","ccc"};
        Stream<String> stream7 = Stream.of(arr);
    }
}

2. Stream流中的常用方法

  1. 逐一处理:forEach
  • Stream流中的常用方法:forEach
    • void forEach(Consumer<? super T> action);
  • 该方法接收一个Consumer接口函数,会将每一个流元素交给函数进行处理。
  • Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据。
  • 注意:
    • forEach方法,用来遍历流中的数据。
    • 是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法。
import java.util.stream.Stream;

public class Test {
    public static void main(String[] args) {
        String[] strings = {"朱古力","猪猪侠","猪猪猪"};
        //使用forEach遍历输出数组
        Stream.of(strings).forEach(name->System.out.println(name));
    }
}
  1. 过滤:filter
  • Stream流中的常用方法:filter,用于对Stream流中的数据进行过滤。
    • Stream<T> filter(Predicate<? super T> predicate);
  • filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤。
  • Predicate中的抽象方法:
    • boolean test(T t);
import java.util.stream.Stream;

public class Test {
    public static void main(String[] args) {
        String[] strings = {"朱古力","猪猪侠","猪猪猪"};
        //使用filter过滤"朱"开头的字符串
        Stream<String> stream = Stream.of(strings).filter(name -> name.startsWith("朱"));
        //输出过滤后的字符串
        stream.forEach(name -> System.out.println(name)); // 朱古力
    }
}
  • Stream流的特点:
    • Stream流属于管道流,只能被消费一次。
    • 第一个Stream流调用完毕方法,数据就会流转到下一个Stream上,而这时第一个Stream流已经使用完毕,就会关闭了,所以第一个Stream流就不能再调用方法了。
  1. 映射:map
  • 如果需要将流中的元素映射到另一个流中,可以使用map方法。
  • <R> Stream<R> map(Function<? super T, ? extends R> mapper);
  • 该参数需要一个Function函数式接口,可以将当前流中的T类型数据转换为另一种R类型的流。
  • Function中的抽象方法:
    • R apply(T t);
import java.util.stream.Stream;

public class Test {
    public static void main(String[] args) {
        String[] strings = {"1","2","3","4","5"};
        //使用map将String映射为Integer
        Stream<Integer> integerStream = Stream.of(strings).map(str -> Integer.parseInt(str));
        //遍历
        integerStream.forEach(i -> System.out.println(i + 1));
    }
}
  1. 统计个数:count
  • Stream流中的常用方法count:用于统计Stream流中元素的个数。
    • long count();
  • count方法是一个终结方法,返回值是一个long类型的整数,所以不能再继续调用Stream流中的其他方法了。
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //使用count统计元素的个数
        long count = list.stream().count();
        System.out.println(count); // 5
    }
}
  1. 取用前几个:limit
  • Stream流中的常用方法:limit用于截取流中的元素。

  • limit方法可以对流进行截取,只取用前n个。

    • Stream<T> limit(long maxSize);
    • 参数是一个long型,如果集合当前长度大于参数则进行截取,否则不进行操作。
  • limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法。

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //使用limit截取前三个元素
        list.stream().limit(3).forEach(i -> System.out.println(i));
    }
}
  1. 跳过前几个:skip
  • Stream流中的常用方法:skip用于跳过元素。
  • 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流。
    • Stream<T> skip(long n);
    • 如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空流。
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //使用skip跳过前三个元素
        list.stream().skip(3).forEach(i -> System.out.println(i));
    }
}
  1. 组合:concat
  • Stream流中的常用方法:concat用于把流组合到一起。
  • 如果有两个流,希望合并称为一个流,那么可以使用Stream流中的静态方法concat
    • static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b);
import java.util.stream.Stream;

public class Test {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("zhuguli", "zhuzhuxia", "zhuzhuzhu");
        Stream<String> stream2 = Stream.of("朱古力", "猪猪侠", "猪猪猪");
        //使用concat合并两个流
        Stream<String> stream = Stream.concat(stream1, stream2);
        //遍历
        stream.forEach(name -> System.out.println(name));
    }
}

3. 练习

/*
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,依次进行以下若干操作步骤: 
  1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。 
  2. 第一个队伍筛选之后只要前2个人;存储到一个新集合中。 
  3. 第二个队伍只要姓朱的成员姓名;存储到一个新集合中。 
  4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。 
  5. 将两个队伍合并为一个队伍;存储到一个新集合中。 
  6. 根据姓名创建 Person 对象;存储到一个新集合中。 
  7. 打印整个队伍的Person对象信息。
*/
public class Person {
    private String name;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

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

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class Test {
    public static void main(String[] args) {
        List<String> oneTeam = new ArrayList<>();
        oneTeam.add("朱古力");
        oneTeam.add("朱古");
        oneTeam.add("朱古力力");
        oneTeam.add("猪猪侠");
        oneTeam.add("猪猪猪");

        List<String> twoTeam = new ArrayList<>();
        twoTeam.add("朱古古");
        twoTeam.add("朱力力");
        twoTeam.add("朱猪侠");
        twoTeam.add("猪猪侠");
        twoTeam.add("猪猪猪");

        // 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        // 2. 第一个队伍筛选之后只要前2个人;存储到一个新集合中。
        List<String> newOneTeam = new ArrayList<>();
        oneTeam.stream().filter(name -> name.length()==3).limit(2).forEach(name -> newOneTeam.add(name));
        // 3. 第二个队伍只要姓朱的成员姓名;存储到一个新集合中。
        // 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        List<String> newTwoTeam = new ArrayList<>();
        twoTeam.stream().filter(name -> name.startsWith("朱")).skip(2).forEach(name -> newTwoTeam.add(name));
        // 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        List<String> newTeam = new ArrayList<>();
        Stream<String> newOneTeamStream = newOneTeam.stream();
        Stream<String> newTwoTeamStream = newTwoTeam.stream();
        Stream.concat(newOneTeamStream,newTwoTeamStream).forEach(name -> newTeam.add(name));
        // 6. 根据姓名创建 Person 对象;存储到一个新集合中。
        List<Person> people = new ArrayList<>();
        newTeam.stream().forEach(name -> people.add(new Person(name)));
        // 7. 打印整个队伍的Person对象信息。
        people.stream().forEach(person -> System.out.println(person));

    }
}

二、方法引用

1. 方法引用符

  • 双冒号::为引用运算符,而它所在的表达式被称为方法引用。如果 Lambda 要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为 Lambda 的替代者。
//定义一个函数式接口
public interface PrintTable {
    public abstract void print(String str);
}

//测试类
public class Test {
    public static void printString(PrintTable p) {
        p.print("Hello,World");
    }

    public static void main(String[] args) {
        //因为PrintTable是一个函数式接口,所以可以使用Lambda表达式作为参数
        printString((String str)->{
            System.out.println(str);
        });

        /*
        分析:
            Lambda表达式的目的就是打印参数传递的字符串
            把参数str传递给了System.out对象,调用对象中的方法println对字符串进行了输出
        注意:
            1.System.out对象已经存在
            2.println方法也已经存在
            所以可以使用方法引用来优化Lambda表达式
            可以使用System.out对象直接引用(调用)println方法
     */

        printString(System.out::println);
    }
}

2. 通过对象名引用成员方法

  • 使用前提:对象名已经存在,成员方法也已经存在,就可以使用对象名引用成员方法。
//函数式接口
public interface PrintTable {
    public abstract void print(String str);
}

//对象类
public class MethodRefObject {
    //大写输出参数传递的字符串
    public void printUpperCaseString(String str) {
        System.out.println(str.toUpperCase());
    }
}

//测试类
public class Test {

    public static void printTable(PrintTable p) {
        p.print("hello");
    }

    public static void main(String[] args) {
        //方法printTable的参数是一个函数式接口,所以可以使用Lambda表达式
        printTable((s)->{
            //创建MethodRefObject对象
            MethodRefObject obj = new MethodRefObject();
            //调用成员方法printUpperCaseString
            obj.printUpperCaseString(s);
        });

        //创建MethodRefObject对象
        MethodRefObject obj = new MethodRefObject();
        //使用方法引用优化Lambda表达式
        printTable(obj::printUpperCaseString);
    }
}

3. 通过类名引用静态方法

  • 前提:类已经存在,静态成员方法也已经存在,就可以通过类名直接引用静态成员方法。
@FunctionalInterface
public interface Calcable {
    public int calcAbs(int number);
}

public class Test {
    public static int calc(int num, Calcable calc){
        return calc.calcAbs(num);
    }

    public static void main(String[] args) {
        //使用lambda表达式
        int rs1 = calc(-5, (int num) -> {
            return Math.abs(num);
        });
        System.out.println(rs1);

        //使用方法引用优化lambda表达式
        int rs2 = calc(-5, Math::abs);
        System.out.println(rs2);
    }
}

4. 通过super引用父类的成员方法

@FunctionalInterface
public interface Greetable {
    public abstract void greet();
}

//父类
public class Humen {
    public void sayHello(){
        System.out.println("Hello,is Humen");
    }
}

public class Men extends Humen {

    @Override
    public void sayHello() {
        System.out.println("Hello,is Men");
    }

    public void method(Greetable g) {
        g.greet();
    }

    public void show(){
        //使用lambda表达式
        method(()->{
            Humen h = new Humen();
            h.sayHello();
        });
        //使用super调用父类方法
        method(()->{
            super.sayHello();
        });
        //使用super引用父类方法
        method(super::sayHello);
    }

    public static void main(String[] args) {
        new Men().show();
    }
}

5. 使用this引用本类成员方法

@FunctionalInterface
public interface Richable {
    public void buy();
}

public class Husbend {
    public void buyHouse(){
        System.out.println("Buy a house!");
    }

    public void marry(Richable r) {
        r.buy();
    }

    //内部调用marry方法
    public void soHappy(){
        //使用lambda表达式
        marry(()->{
            //使用this调用本类方法
            this.buyHouse();
        });

        //使用this引用本类方法
        marry(this::buyHouse);
    }

    public static void main(String[] args) {
        new Husbend().soHappy();
    }
}

6. 类的构造器引用

  • 格式:类名称::new
public class Person {
    private String name;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

@FunctionalInterface
public interface PersonBuilder {
    Person getPerson(String name);
}

public class Test {
    public static void printName(String name, PersonBuilder pb) {
        Person person = pb.getPerson(name);
        System.out.println(person.getName());
    }

    public static void main(String[] args) {
        //使用lambda表达式
        printName("zhuguli",(name)->{
            return new Person(name);
        });

        //通过引用类的构造器
        printName("zhuzhuxia",Person::new);
    }
}

7. 数组的构造器引用

  • 格式:int[]::new
@FunctionalInterface
public interface ArrayBuilder {
    int[] arrayBuilder(int length);
}

public class Test {
    public static int[] getArray(int length, ArrayBuilder ab){
        return ab.arrayBuilder(length);
    }

    public static void main(String[] args) {
        //使用lambda表达式
        int[] arr1 = getArray(10,(length)->{
            return new int[length];
        });
        System.out.println(arr1.length);

        //通过引用数组的构造器
        int[] arr2 = getArray(20,int[]::new);
        System.out.println(arr2.length);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值