不可变集合、Stream流、方法引用

一、不可变集合

是一个长度不可变,内容也无法修改的集合

定义完成后不可以修改,或者添加、删除

不可变集合分类

  • 不可变的list集合

  • 不可变的set集合

  • 不可变的map集合

如何创建不可变集合?
        List、Set、Map接口中,都存在of方法可以创建不可变集合

三种方式的细节

  • List:直接用
  • Set:元素不能重复
  • Map:元素不能重复、键值对数量最多是10个。

                                超过10个用ofEntries方法,JDK10以后出了一个Map.copyof(hm)方法,直接就可以使用了

 List<String> list = List.of("张三", "李四", "王五", "赵六");
 Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六"); Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六");
Map<String, String> map = Map.of("张三", "南京", "张三", "北京", "王五", "上海",
                "赵六", "广州", "孙七", "深圳", "周八", "杭州",
                "吴九", "宁波", "郑十", "苏州", "刘一", "无锡",
                "陈二", "嘉兴");

        //获取到所有的键值对对象(Entry对象)
        Set<Map.Entry<String, String>> entries = hm.entrySet();
        //把entries变成一个数组
        Map.Entry[] arr1 = new Map.Entry[0];
        //toArray方法在底层会比较集合的长度跟数组的长度两者的大小
        //如果集合的长度 > 数组的长度 :数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
        //如果集合的长度 <= 数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用
        Map.Entry[] arr2 = entries.toArray(arr1);
        //不可变的map集合
        Map map = Map.ofEntries(arr2);
        

//或者
Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));


//或者
Map<String, String> map = Map.copyOf(hm);

二、Stream流

结合Lambda表达式,简化集合数组的操作

使用步骤:

1.先得到一条Stream流(流水线),并把数据放上去
2.利用Stream流中的API进行各种操作
过滤、转换        中间方法            方法调用完毕之后,还可以调用其他方法

统计、打印        终结方法            最后一步,调用完毕之后,不能调用其他方法
2.使用中间方法对流水线上的数据进行操作
3.使用终结方法对流水线上的数据进行操作


   1.获取Stream流

获取方式方法名说明
单列集合default Stream<E> stream()collection中的默认方法
双列集合先使用keySet ()
entrySet ()
获取单列集合
无法直接使用stream流
数组public static <T> Stream<T> stream(T[]array)Arrays工具类中的静态方法
一堆零散数据public static<T> Stream<T> of(T... values)Stream接口中的静态方法
public class StreamDemo {
    public static void main(String[] args) {
        //Collection体系的集合可以使用默认方法stream()生成流
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();

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

        //Map体系的集合间接的生成流
        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();

        //数组可以通过Arrays中的静态方法stream生成流
        String[] strArray = {"hello","world","java"};
        Stream<String> strArrayStream = Arrays.stream(strArray);
      
      	//同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流
        Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
        Stream<Integer> intStream = Stream.of(10, 20, 30);
    }
}

2.Stream流中间操作方法

方法名说明
Stream<T> filter(Predicate predicate)过滤
Stream<T> limit(long maxSize)获取前几个元素
Stream<T> skip(long n)跳过前几个元素
static <T> Stream<T> concat(Stream a, Stream b)合并a和b两个流为一个流
Stream<T> distinct()元素去重,依赖(hashCode和equals方法)
Stream<R> map(Function<T , R> mapper)转换流中的数据类型

注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据
filter代码演示

public class MyStream3 {
    public static void main(String[] args) {
//        Stream<T> filter(Predicate predicate):过滤
//        Predicate接口中的方法	boolean test(T t):对给定的参数进行判断,返回一个布尔值

        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        //filter方法获取流中的 每一个数据.
        //而test方法中的s,就依次表示流中的每一个数据.
        //我们只要在test方法中对s进行判断就可以了.
        //如果判断的结果为true,则当前的数据留下
        //如果判断的结果为false,则当前数据就不要.
//        list.stream().filter(
//                new Predicate<String>() {
//                    @Override
//                    public boolean test(String s) {
//                        boolean result = s.startsWith("张");
//                        return result;
//                    }
//                }
//        ).forEach(s-> System.out.println(s));

        //因为Predicate接口中只有一个抽象方法test
        //所以我们可以使用lambda表达式来简化
//        list.stream().filter(
//                (String s)->{
//                    boolean result = s.startsWith("张");
//                        return result;
//                }
//        ).forEach(s-> System.out.println(s));

        list.stream().filter(s ->s.startsWith("张")).forEach(s-> System.out.println(s));

    }
}

limit&skip代码演示

public class StreamDemo02 {
    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个数据在控制台输出
        list.stream().limit(3).forEach(s-> System.out.println(s));
        System.out.println("--------");

        //需求2:跳过3个元素,把剩下的元素在控制台输出
        list.stream().skip(3).forEach(s-> System.out.println(s));
        System.out.println("--------");

        //需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
        list.stream().skip(2).limit(2).forEach(s-> System.out.println(s));
    }
}

concat&distinct代码演示

public class StreamDemo03 {
    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> s1 = list.stream().limit(4);

        //需求2:跳过2个数据组成一个流
        Stream<String> s2 = list.stream().skip(2);

        //需求3:合并需求1和需求2得到的流,并把结果在控制台输出
//        Stream.concat(s1,s2).forEach(s-> System.out.println(s));

        //需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
        Stream.concat(s1,s2).distinct().forEach(s-> System.out.println(s));
    }
}

3.Stream流终结操作方法

方法名说明
void forEach(Consumer action)遍历
long count()统计个数
toArray()收集流中数据,放到数组中
collect(Collector  collector)收集流中数据,放到集合中
public class MyStream5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        //method1(list);
        
//        long count():返回此流中的元素数
        long count = list.stream().count();
        System.out.println(count);
    }

    private static void method1(ArrayList<String> list) {
        //  void forEach(Consumer action):对此流的每个元素执行操作
        //  Consumer接口中的方法void accept(T t):对给定的参数执行此操作
        //在forEach方法的底层,会循环获取到流中的每一个数据.
        //并循环调用accept方法,并把每一个数据传递给accept方法
        //s就依次表示了流中的每一个数据.
        //所以,我们只要在accept方法中,写上处理的业务逻辑就可以了.
        list.stream().forEach(
                new Consumer<String>() {
                    @Override
                    public void accept(String s) {
                        System.out.println(s);
                    }
                }
        );
      
        System.out.println("====================");
        //lambda表达式的简化格式
        //是因为Consumer接口中,只有一个accept方法
        list.stream().forEach(
                (String s)->{
                    System.out.println(s);
                }
        );
        System.out.println("====================");
        //lambda表达式还是可以进一步简化的.
        list.stream().forEach(s->System.out.println(s));
    }
}

collection应用:

// toList和toSet方法演示 
public class MyStream7 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list1.add(i);
        }

        list1.add(10);
        list1.add(10);
        list1.add(10);
        list1.add(10);
        list1.add(10);

        //filter负责过滤数据的.
        //collect负责收集数据.
                //获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
        //Collectors.toList() : 在底层会创建一个List集合.并把所有的数据添加到List集合中.
        List<Integer> list = list1.stream().filter(number -> number % 2 == 0)
                .collect(Collectors.toList());

        System.out.println(list);

    Set<Integer> set = list1.stream().filter(number -> number % 2 == 0)
            .collect(Collectors.toSet());
    System.out.println(set);
}
}
/**
Stream流的收集方法 toMap方法演示
创建一个ArrayList集合,并添加以下字符串。字符串中前面是姓名,后面是年龄
"zhangsan,23"
"lisi,24"
"wangwu,25"
保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
*/
public class MyStream8 {
	public static void main(String[] args) {
      	ArrayList<String> list = new ArrayList<>();
        list.add("zhangsan,23");
        list.add("lisi,24");
        list.add("wangwu,25");

        Map<String, Integer> map = list.stream().filter(
                s -> {
                    String[] split = s.split(",");
                    int age = Integer.parseInt(split[1]);
                    return age >= 24;
                }

         //   collect方法只能获取到流中剩余的每一个数据.
         //在底层不能创建容器,也不能把数据添加到容器当中

         //Collectors.toMap 创建一个map集合并将数据添加到集合当中

          // s 依次表示流中的每一个数据

          //第一个lambda表达式就是如何获取到Map中的键
          //第二个lambda表达式就是如何获取Map中的值
        ).collect(Collectors.toMap(
                s -> s.split(",")[0],
                s -> Integer.parseInt(s.split(",")[1]) ));

        System.out.println(map);
	}
}

总结:

1. Stream流的作用
结合了Lambda表达式,简化集合、数组的操作

2. Stream的使用步骤
获取Stream流对象使用中间方法处理数据●使用终结方法处理数据

3.如何获取Stream流对象

  • 单列集合:Collection中的默认方法stream
  • 双列集合:不能直接获取
  • 数组: Arrays工具类型中的静态方法stream
  • 一堆零散的数据:Stream接口中的静态方法of

4.常见方法
中间方法:
filter,limit,skip, distinct,concat,map
终结方法:
forEach,count,collect


三、方法引用

1.什么是方法引用?
把已经存在的方法拿过来用,当做函数式接口中抽象方法的方法体
2.︰︰是什么符号?
方法引用符
3.方法引用时要注意什么?

  • 需要有函数式接口
  • 被引用方法必须已经存在
  • 被引用方法的形参和返回值需要跟抽象方法保持一致
  • 被引用方法的功能要满足当前的需求

4.方法引用的分类

  1. 引用静态方法 
  2. 引用成员方法
  • 引用其他类的成员方法
  • 引用本类的成员方法
  • 引用父类的成员方

        3.引用构造方法

        4.其他调用方式

  • 使用类名引用成员方法
  • 引用数组的构造方法

1.引用静态方法


类名::静态方法

package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Collections;

public class FunctionDemo2 {
    public static void main(String[] args) {
       /*
        方法引用(引用静态方法)
        格式
              类::方法名

        需求:
            集合中有以下数字,要求把他们都变成int类型
            "1","2","3","4","5"
       */


        //1.创建集合并添加元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"1","2","3","4","5");

        //2.把他们都变成int类型
       /* list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                int i = Integer.parseInt(s);
                return i;
            }
        }).forEach(s -> System.out.println(s));*/



        //1.方法需要已经存在
        //2.方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
        //3.方法的功能需要把形参的字符串转换成整数

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



    }
}

2.引用成员方法


对象∶:成员方法

this: :成员方法

super: :成员方法

package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Collections;

public class FunctionDemo3  {
    public static void main(String[] args) {
        /*
        方法引用(引用成员方法)
        格式
                其他类:其他类对象::方法名
                本类:this::方法名(引用处不能是静态方法)
                父类:super::方法名(引用处不能是静态方法)
        需求:
            集合中有一些名字,按照要求过滤数据
            数据:"张无忌","周芷若","赵敏","张强","张三丰"
            要求:只要以张开头,而且名字是3个字的

       */

        //1.创建集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
        //3.过滤数据(只要以张开头,而且名字是3个字的)
        //list.stream().filter(s->s.startsWith("张")).filter(s->s.length() == 3).forEach(s-> System.out.println(s));


      /*  list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张") && s.length() == 3;
            }
        }).forEach(s-> System.out.println(s));*/

      /*  StringOperation so = new StringOperation();
        list.stream().filter(so::stringJudge)
                .forEach(s-> System.out.println(s));*/

        //静态方法中是没有this的
        list.stream().filter(new FunctionDemo3()::stringJudge)
                .forEach(s-> System.out.println(s));

    }


    public boolean stringJudge(String s){
        return s.startsWith("张") && s.length() == 3;
    }
}

3.引用构造方法


类名: : new

package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class FunctionDemo4 {
    public static void main(String[] args) {
        /*
        方法引用(引用构造方法)
        格式
                类名::new

        目的:
                创建这个类的对象

        需求:
             集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中

        方法引用的规则:
            1.需要有函数式接口
            2.被引用的方法必须已经存在
            3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致
            4.被引用方法的功能需要满足当前的需求
       */

        //1.创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
        //3.封装成Student对象并收集到List集合中
        //String --> Student
      /*  List<Student> newList = list.stream().map(new Function<String, Student>() {
            @Override
            public Student apply(String s) {
                String[] arr = s.split(",");
                String name = arr[0];
                int age = Integer.parseInt(arr[1]);
                return new Student(name, age);
            }
        }).collect(Collectors.toList());
        System.out.println(newList);*/


        List<Student> newList2 = list.stream().map(Student::new).collect(Collectors.toList());
        System.out.println(newList2);

    }
}

4.使用类名引用成员方法


类名::成员方法

package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Collections;

public class FunctionDemo5 {
    public static void main(String[] args) {
        /*
        方法引用(类名引用成员方法)
        格式
                类名::成员方法
        需求:
             集合里面一些字符串,要求变成大写后进行输出


        方法引用的规则:
        1.需要有函数式接口
        2.被引用的方法必须已经存在
        3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
        4.被引用方法的功能需要满足当前的需求

        抽象方法形参的详解:
        第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法
                    在Stream流当中,第一个参数一般都表示流里面的每一个数据。
                    假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法

        第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法

        局限性:
            不能引用所有类中的成员方法。
            是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。

       */

        //1.创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list, "aaa", "bbb", "ccc", "ddd");
        //3.变成大写后进行输出
        //map(String::toUpperCase)
        //拿着流里面的每一个数据,去调用String类中的toUpperCase方法,方法的返回值就是转换之后的结果。
        list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));


        //String --> String
       /* list.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        }).forEach(s -> System.out.println(s));*/

    }
}

5.引用数组的构造方法


数据类型::new

package com.itheima.a01myfunction;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class FunctionDemo6 {
    public static void main(String[] args) {
        /*
        方法引用(数组的构造方法)
        格式
                数据类型[]::new
        目的:
                创建一个指定类型的数组
        需求:
             集合中存储一些整数,收集到数组当中

        细节:
            数组的类型,需要跟流中数据的类型保持一致。

       */

        //1.创建集合并添加元素
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5);
        //2.收集到数组当中

        Integer[] arr2 = list.stream().toArray(Integer[]::new);
        System.out.println(Arrays.toString(arr2));

        /*Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {
            @Override
            public Integer[] apply(int value) {
                return new Integer[value];
            }
        });*/
        //3.打印
    }
}


 

  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值