java回顾 Lambda表达式和Stream流

时间:2021-07-30作者:klpeng分类:Java技术浏览:107评论:0

Lambda表达式

函数式编程思想概述

面向对象思想:
​ 做一件事情,找一个能解决这个事情的对象,调用对象的方法来完成这件事情

函数式的编程思想:
​ 重视的是结果,怎么做事情,不重视完成的过程,找谁做

使用Lambda表达式简化匿名内部类

Lambda表达式的标准格式与省略格式(重点)
()->{}
(参数)->{重写抽象方法的方法体}
(int a,int b)->{}>省略数据类型 (a,b)->{}
(int a)->{}
>省略数据类型 a->{}
a->{只有一行代码}==>a->代码(return ; {} 一起省略)

public class Demo01Lambda {    
  public static void main(String[] args) {        
		//使用匿名内部类创建一个新的线程        
		new Thread(new Runnable() {            
		@Override            
		public void run() {                
		System.out.println(Thread.currentThread().getName()+"-->使用匿名内			部类的方式,实现多线程程序!");            
			}        
		}).start();//使用Lambda表达式简化匿名内部类的书写        
	     new Thread(()->{
	     System.out.println(Thread.currentThread()..getName()+"使用Lambda表达式,实现多线程程序")
	     }).start();
	}
}

Lambda表达式的语法

Lambda表达式的语法:

​ 由一些参数,一个箭头,一些代码组成

格式:
写的是@Override里的方法
(参数列表)->{重写抽象方法的方法体}

Lambda表达式作用:简化匿名内部类

lambda表达式使用前提:必须有接口,接口中有且只能有一个抽象方法(函数式接口)

Lambda表达式是可推导,可省略:能推导出来,Lambda表达式重写的就是接口中唯一的抽象方法

  • (参数列表):重写抽象方法的参数
  • ->:传递(固定写法):可以把参数传递到方法体中使用
  • {}:重写抽象方法的方法体

使用Lambda表达式重写有参数有返回值的方法

/*
    使用Lambda表达式重写有参数有返回值的方法
    需求:
        创建一个数组,数据类型使用Person
        创建3个Person对象,存储到数组中
        使用Arrays数组工具类中的sort方法,根据比较器产生的比较规则对Person对象进行排序(年龄升序)
 */
public class Demo02Lambda {
    public static void main(String[] args) {
        //创建一个数组,数据类型使用Person
        Person[] arr = new Person[3];
        //创建3个Person对象,存储到数组中
        arr[0] = new Person("张三",18);
        arr[1] = new Person("李四",20);
        arr[2] = new Person("王五",19);
        System.out.println("排序前,数组中的元素:"+ Arrays.toString(arr));
        //使用Arrays数组工具类中的sort方法,根据比较器产生的比较规则对Person对象进行排序(年龄升序)
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //升序:o1-o2  降序:o2-o1
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println("排序后,数组中的元素:"+ Arrays.toString(arr));

        //使用lambda表达式简化匿名内部类
        Arrays.sort(arr,(Person o1, Person o2)->{
            return o2.getAge()-o1.getAge();
        });
        System.out.println("排序后,数组中的元素:"+ Arrays.toString(arr));
    }
}

Lambda表达式简化格式

/*
    Lambda表达式简化格式:
    Lambda表达式的使用前提:有接口,接口中有且仅有一个抽象方法
    Lambda表达式是可推导,可省略的
    可以推导出,Lambda表达式重写的就是接口中唯一的抽象方法
    也可以推导出方法的参数和方法有没有返回值,所以可以对参数和返回值在进行简化
    格式:
        (参数列表)->{重写抽象方法的方法体}
    1.(参数列表):参数列表的数据类型是可以推导出来的,可以省略
        (int a)-->(a)
        (int a,String s)-->(a,s)
    2.(参数列表):参数列表中的参数只有一个,()括号也可以省略,但是参数列表没有参数()不能省略
        (int a)-->(a)-->a
        ()-->不能省略()
    3.{重写抽象方法的方法体}:重写的方法体,只有一行代码,无论是否有返回值
        { }和一行代码的分号和return关键字都可以省略,但是必须一起省略
 */
public class Demo03Lambda {
    public static void main(String[] args) {
        //使用Lambda表达式简化实现多线程的匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类的方式");
            }
        }).start();

        new Thread(()->{
            System.out.println("lambda表达式的方式");
        }).start();

        //简化Lambda表达式
        new Thread(()->System.out.println("lambda表达式的方式")).start();
        System.out.println("---------------------------");

        //创建一个数组,数据类型使用Person
        Person[] arr = new Person[3];
        //创建3个Person对象,存储到数组中
        arr[0] = new Person("张三",18);
        arr[1] = new Person("李四",20);
        arr[2] = new Person("王五",19);
        System.out.println("排序前,数组中的元素:"+ Arrays.toString(arr));

        //使用lambda表达式简化匿名内部类
        Arrays.sort(arr,(Person o1, Person o2)->{
            return o2.getAge()-o1.getAge();
        });
        System.out.println("排序后,数组中的元素:"+ Arrays.toString(arr));

        //简化Lambda表达式
        Arrays.sort(arr,(o1,o2)-> o2.getAge()-o1.getAge());
    }
}

Stream流

Stream流的基本概述

我们可以把集合|数组,转换为Stream流,使用Stream流中的方法,对集合|数组进行操作

Stream流的基本体验

使用传统方法对集合进行操作

/*
    使用传统技术对集合进行操作
 */
public class Demo01ArrayList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        //1. 首先筛选所有姓张的人;把姓张的人存储到一个新的集合中
        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if(name.startsWith("张")){
                zhangList.add(name);
            }
        }
        //System.out.println(zhangList);//[张无忌, 张强, 张三丰]

        //2. 然后筛选名字有三个字的人;把名字有三个字的人存储到一个新的集合中
        List<String> threeList = new ArrayList<>();
        for (String name : zhangList) {
            if(name.length()==3){
                threeList.add(name);
            }
        }
        //System.out.println(threeList);//[张无忌, 张三丰]

        //3. 最后进行对结果进行打印输出。
        for (String name : threeList) {
            System.out.println(name);
        }
    }
}

使用stream流对集合进行操作

/*
    使用传统技术对集合进行操作
 */
public class Demo01ArrayList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

		 //1. 首先筛选所有姓张的人;把姓张的人存储到一个新的集合中
        //2. 然后筛选名字有三个字的人;把名字有三个字的人存储到一个新的集合中
        //3. 最后进行对结果进行打印输出。
        //把集合转换为Stream流
		list.stream()
				.filter(name->name.startWith("张"))//判断是否以张开头
				.filter(name0>name.length()==3)//判断元素是否等于3
				.forEach(name->System.out.println(name));//遍历集合
		
    }
}

流式思想概述

java回顾 Lambda表达式和Stream流

获取stream流的方式(重点)

/*
    获取Stream流的方式(重点)
        1.在Collection接口中,定义了一个方法stream,可以把集合转换为Stream流
            default Stream<E> stream()  此方法只能是Collection接口下的单列集合可以使用
        2.使用Stream接口中的方法of,可以把可变参数(数组)转换为Stream流
            static <T> Stream<T> of(T... values)  方法的参数是一个可变参数,可以传递数组
 */
public class Demo03Stream {
	public static void main(String[] args){
		show02();
	}
	
	/*
        把数组转换为Stream流
        注意:
            可变参数,可以传递任意个
     */
	private static void show02(){
		Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
		Stream<String> stream2 = Stream.of("aaa", "bbb", "ccc");
		//可变参数底层就是一个数组,of方法参数也可以传递数组
        String[] arr1 = {"aaa", "bbb", "ccc"};
        Stream<String> stream3 = Stream.of(arr1);
        //注意:数组的类型必须使用包装类
        Integer[] arr3 = {1,2,3};
        Stream<Integer> stream5 = Stream.of(arr3);
	}
	/*
        把集合转换为Stream流
     */
    private static void show01() {
        ArrayList<Integer> list = new ArrayList<>();
        //把ArrayList集合转换为Stream流
        Stream<Integer> stream1 = list.stream();

        LinkedList<String> linked = new LinkedList<>();
        //把LinkedList集合转换为Stream流
        Stream<String> stream2 = linked.stream();

        HashSet<Double> set = new HashSet<>();
        //把HashSet集合转换为Stream流
        Stream<Double> stream3 = set.stream();

        HashMap<String,String> map = new HashMap<>();
        //map.stream();//Cannot resolve method 'stream()' Map集合是不能直接转换为Stream流

        Set<String> keySet = map.keySet();
        //把Map集合中所有的key转换为Stream流
        Stream<String> stream4 = keySet.stream();

        Collection<String> values = map.values();
        //把Map集合中所有的value转换为Stream流
        Stream<String> stream5 = values.stream();

        Set<Map.Entry<String, String>> entries = map.entrySet();
        //把Map集合中所有的Entry对象转换为Stream流
        Stream<Map.Entry<String, String>> stream6 = entries.stream();
   }     
}

Stream的常用方法

流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。本小节中,终结方法包括countforEach方法。
  • 非终结方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为非终结方法。)

函数拼接与终结方法

在上述介绍的各种方法中,凡是返回值仍然为Stream接口的为函数拼接方法,它们支持链式调用;而返回值不再为Stream接口的为终结方法,不再支持链式调用。如下表所示:java回顾 Lambda表达式和Stream流
备注:本小节之外的更多方法,请自行参考API文档。

forEach方法:用于遍历

/*
    Stream流中常用方法
    forEach:用于遍历Stream流中的元素
        void forEach(Consumer<? super T> action) 对此流的每个元素执行操作。
        参数:
            Consumer<? super T> action:是一个消费型接口
            抽象方法:
                void accept(T t)  :消费一个指定泛型类型的数据
    注意:
        forEach方法是一个终结方法,没有返回值;也不能使用链式编程继续调用Stream流中的其他方法了
 */
public class Demo04forEach {
    public static void main(String[] args) {
        //获取Stream流
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        //使用forEach方法遍历Stream流中的元素
        stream.forEach((Integer in)->{
            System.out.println(in);
        });

        /*
            IllegalStateException: stream has already been operated upon or closed 非法状态异常
            Stream流只能使用一次,使用完毕就会流向到下一个Stream流对象
            之前的Stream流对象就会被销毁,就不能在使用了,否则会抛出异常
         */
        //简化Lambda表达式
        stream.forEach(i-> System.out.println(i));
    }
}

Count方法:统计个数

/*
    Stream流中常用方法
    count:用于统计Stream流中元素个数
    long count();
    注意:
        count方法是一个终结方法,返回值类型是long,不能继续调用Stream中的方法了
 */
public class Demo05count {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        //把ArrayList集合转换为Stream流
        Stream<String> stream = list.stream();
        //使用Stream流中的count方法,统计元素个数
        long count = stream.count();
        System.out.println(count);//4
    }
}

filter方法:过滤

/*
    Stream流中常用方法
    filter:用于过滤Stream流中的元素
        Stream<T> filter(Predicate<? super T> predicate);
        参数:
            Predicate<? super T> predicate接口
            抽象方法:
                boolean test(T t)  用于对接口指定泛型类型的数据进行判断
    注意:
        filter方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
 */
public class Demo06filter {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊", "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //使用filter方法过滤Stream流中的元素,只要包含羊羊的
       /* Stream<String> stream2 = stream.filter((String s) -> {
            return s.contains("羊羊");
        });
        //遍历stream2流
        stream2.forEach(s-> System.out.println(s));*/

       //链式编程
       stream.filter(s->s.contains("羊羊")).forEach(s-> System.out.println(s));
    }
}

limit方法:获取前n个元素

/*
    Stream流中常用方法
    limit:获取前n个元素
        Stream<T> limit(long maxSize);
    注意:
        limit方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
 */
public class Demo07limit {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊",
                "慢羊羊", "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        //使用limit方法,获取Stream流中的前4个元素,存储到一个新的Stream流中返回
        //Stream<String> stream2 = stream.limit(4);
        //遍历stream2流
        //ream2.forEach(s-> System.out.println(s));

        //链式编程
        stream.limit(4).forEach(s -> System.out.println(s));
    }
}

skip方法:跳过前n个元素

/*
    Stream流中常用方法
    skip:跳过前n个元素
    Stream<T> skip(long n);
    注意:
        1.skip方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
        2.skip跳过的元素数量大于流中元素的数量,返回一个没有元素的空流
 */
public class Demo08skip {
    public static void main(String[] args) {
        //创建Stream流对象
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        //使用skip方法跳过前6个元素,把剩余的元素存储到一个新的Stream流中返回
        //Stream<Integer> stream2 = stream.skip(6);
        //遍历stream2
        //ream2.forEach(s-> System.out.println(s));

        //ream.skip(6).forEach(s-> System.out.println(s));

        long count = stream.skip(6).count();
        System.out.println(count);//4
    }
}

map方法:映射类型转换

/*
    Stream流中常用方法
    map方法:映射,把Stream流中的元素,转换为另外一种数据类型的流(类型转换)
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    参数:
        Function<T, R> mapper
    抽象方法:
        R apply(T t),根据类型T的参数获取类型R的结果。用于数类型转换
    注意:
        map方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
 */
public class Demo09map {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("11", "22", "33", "44");
        //使用Stream流中map方法,把字符串类型的流,转换为Integer类型的流返回
        /*Stream<Integer> integerStream = stream.map((String s) -> {
            return Integer.parseInt(s);
        });*/
        //遍历Stream流
        //integerStream.forEach(s-> System.out.println(s+10));

        //链式编程
        stream.map(s->Integer.parseInt(s)).forEach(s-> System.out.println(s));
    }
}

concat方法:组合

/*
    Stream流中常用方法
    concat方法:组合,可以把两个Stream流,组合为一个新的Stream流
    static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
    注意:
        concat方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
 */
public class Demo10concat {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊", "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
        Stream<String> stream2 = Stream.of("aa", "bb", "cc", "dd", "ee");
        //使用Stream中的静态方法concat把两个Stream组合为一个新的Stream流
        Stream<String> stream = Stream.concat(stream1, stream2);
        //遍历Stream流
        stream.forEach(s-> System.out.println(s));

        Stream<String> stream3 = Stream.of("aa", "bb", "cc", "dd", "ee");
        Stream<Integer> stream4 = Stream.of(1,2,3,4);
        //Stream<Object> concat = Stream.concat(stream3, stream4);
        //concat.forEach(s-> System.out.println(s));
        Stream.concat(stream3,stream4).forEach(s-> System.out.println(s));
    }
}

使用stream流和不使用stream流的区别

不使用stream流

/*
        现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
        2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
        3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
        4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
        5. 将两个队伍合并为一个队伍;
        6. 打印整个队伍的姓名信息。

 */
public class Demo01StreamTest {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
        one.add("欧阳锋");
        one.add("蔡徐坤");

        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");

        //1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
        List<String> one1 = new ArrayList<>();
        for (String s : one) {
            if(s.length()==3){
                one1.add(s);
            }
        }
        //System.out.println(one1);//[宋远桥, 苏星河, 洪七公, 欧阳锋, 蔡徐坤]

        //2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
        List<String> one2 = new ArrayList<>();
        for (int i = 0; i < 3; i++) {//i=0,1,2
            one2.add(one1.get(i));
        }
        //System.out.println(one2);//[宋远桥, 苏星河, 洪七公]

        //3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
        List<String> two1 = new ArrayList<>();
        for (String s : two) {
            if(s.startsWith("张")){
                two1.add(s);
            }
        }
        //System.out.println(two1);//[张无忌, 张三丰, 张二狗, 张天爱, 张三]

        //4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
        List<String> two2 = new ArrayList<>();
        for (int i = 2; i < two1.size(); i++) {
            two2.add(two1.get(i));
        }
        //System.out.println(two2);//[张二狗, 张天爱, 张三]

        //5. 将两个队伍合并为一个队伍;
        List<String> all = new ArrayList<>();
        //Collection集合中的方法boolean addAll(Collection<? extends E> c) 把一个集合中的所有元素,添加到另外一个集合中
        all.addAll(one2);
        all.addAll(two2);

        //6. 打印整个队伍的姓名信息。
        for (String s : all) {
            System.out.println(s);
        }
    }
}

使用stream流

/*
        现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
        2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
        3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
        4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
        5. 将两个队伍合并为一个队伍;
        6. 打印整个队伍的姓名信息。
 */
public class Demo02StreamTest {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
        one.add("欧阳锋");
        one.add("蔡徐坤");

        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");

        //1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
        //2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
        //把one集合转换为Stream流
        Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);

        //3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
        //4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
        //把two集合转换为Stream流
        Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);

        //5. 将两个队伍合并为一个队伍;
        //6. 打印整个队伍的姓名信息。
        Stream.concat(oneStream,twoStream).forEach(s-> System.out.println(s));
    }
}

收集Stream结果(重点)

把Stream流转换为集合或者把Stream流转换为数组

把Stream流转换为集合:收集到集合中

*Stream流转换为集合:收集到集合中
    使用Stream流中的方法:
        R collect(Collector<? super T,A,R> collector)
        参数:
            Collector:是一个接口,需要传递接口的实现类对象
    java.util.stream.Collectors是一个工具类,里边提供的方法,返回了Collector接口的实现类对象
        static <T> Collector<T,?,List<T>> toList():Stream流收集到List集合中
        static <T> Collector<T,?,Set<T>> toSet() :Stream流收集到Set集合中
 */
public class Demo01Stream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊",
                "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰","美羊羊");
        //把Stream流收集到List集合中:转换为List集合(有序,有索引,允许重复)
        //List<String> list = stream.collect(Collectors.toList());
        //System.out.println(list);//[美羊羊, 喜羊羊, 懒羊羊, 慢羊羊, 暖羊羊, 沸羊羊, 灰太狼, 红太狼, 小灰灰]

        //把Stream流收集到Set集合中:转换为Set集合(无序,不包含索引的方法,不允许存储重复元素)
        Set<String> set = stream.collect(Collectors.toSet());
        System.out.println(set);//[美羊羊, 沸羊羊, 红太狼, 灰太狼, 暖羊羊, 小灰灰, 喜羊羊, 懒羊羊, 慢羊羊]
    }
}

把Stream流转换为数组:收集到数组中

/*
    把Stream流转换为数组:收集到数组中
    Stream流中的方法:
        Object[] toArray()
 */
public class Demo02Stream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊",
                "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰","美羊羊");
        Object[] arr = stream.toArray();
        for (Object o : arr) {
            System.out.println(o);
        }
    }
}
打赏
文章版权声明:除非注明,否则均为彭超的博客原创文章,转载或复制请以超链接形式并注明出处。
相关推荐

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

猜你喜欢