day26 不可变集合&&Stream流&&方法引用

1.不可变集合

1.1 什么是不可变集合

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

1.2 使用场景

如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。

当集合对象被不可信的库调用时,不可变形式是安全的。

简单理解:

不想让别人修改集合中的内容

1.3 不可变集合分类

一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作

  • 不可变的list集合

  • 不可变的set集合(唯一性)

  • 不可变的map集合(键不能重复;只能传递20个参数、10个键值对)

1.4 不可变的list集合      

 /*
             创建不可变的List集合
             "张三", "李四", "王五", "赵六"
         */
 ​
         //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
         List<String> list = List.of("张三", "李四", "王五", "赵六");
 //Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六");
         for (String s : list) {
             System.out.println(s);
         }
 ​
         System.out.println("---------------------------");
 ​
         Iterator<String> it = list.iterator();
         while(it.hasNext()){
             String s = it.next();
             System.out.println(s);
         }
         System.out.println("---------------------------");

1.6 不可变的Map集合

1.6.2:键值对个数大于10
 public class ImmutableDemo4 {
     public static void main(String[] args) {
         /*
             创建Map的不可变集合,键值对的数量超过10个
         */
         //1.创建一个普通的Map集合
         HashMap<String, String> hm = new HashMap<>();
         hm.put("张三", "南京");
         hm.put("李四", "北京");
         hm.put("王五", "上海");
         hm.put("赵六", "北京");
         hm.put("孙七", "深圳");
         hm.put("周八", "杭州");
         hm.put("吴九", "宁波");
         hm.put("郑十", "苏州");
         hm.put("刘一", "无锡");
         hm.put("陈二", "嘉兴");
         hm.put("aaa", "111");
 ​
         //2.利用上面的数据来获取一个不可变的集合
 /*
         //获取到所有的键值对对象(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.put("bbb","222");*/
 ​
 ​
         //Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
 ​
         Map<String, String> map = Map.copyOf(hm);
         map.put("bbb","222");
     }
 }

2.Stream流

2.1体验Stream流【理解】

  • 案例需求

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

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

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

    • 把"张"开头的集合中的长度为3的元素存储到一个新的集合

    • 遍历上一步得到的集合

  • 原始方式示例代码

     public class MyStream1 {
         public static void main(String[] args) {
             //集合的批量添加
             ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));
             //list.add()
     ​
             //遍历list1把以张开头的元素添加到list2中。
             ArrayList<String> list2 = new ArrayList<>();
             for (String s : list1) {
                 if(s.startsWith("张")){
                     list2.add(s);
                 }
             }
             //遍历list2集合,把其中长度为3的元素,再添加到list3中。
             ArrayList<String> list3 = new ArrayList<>();
             for (String s : list2) {
                 if(s.length() == 3){
                     list3.add(s);
                 }
             }
             for (String s : list3) {
                 System.out.println(s);
             }      
         }
     }
  • 使用Stream流示例代码

     public class StreamDemo {
         public static void main(String[] args) {
             //集合的批量添加
             ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));
     ​
             //Stream流
             list1.stream().filter(s->s.startsWith("张"))
                     .filter(s->s.length() == 3)
                     .forEach(s-> System.out.println(s));
         }
     }
  • Stream流的好处

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

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

    • 代码简洁

2.3Stream流中间操作方法【应用】

  • 概念

    中间操作的意思是,执行完此方法之后,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()返回由该流的不同元素(根据Object.equals(Object) )组成的流
  • filter代码演示

     public class MyStream3 {
         public static void main(String[] args) {
             ArrayList<String> list = new ArrayList<>();
             list.add("张三丰");
             list.add("张无忌");
             list.add("张翠山");
             list.add("王二麻子");
             list.add("张良");
             list.add("谢广坤");
     ​
             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));
     //林青霞 张曼玉 王祖贤
     ​
         //需求2:跳过3个元素,把剩下的元素在控制台输出
             list.stream().skip(3).forEach(s-> System.out.println(s));
     //柳岩 张敏 张无忌
     ​
        //需求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));
         }
     }

2.4Stream流终结操作方法【应用】

  • 概念

    终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作

  • 常见方法

    方法名说明
    void forEach(Consumer action)//list.stream().forEach(xxx)对此流的每个元素执行操作
    long count()//list.stream().count()返回此流中的元素数

2.5Stream流的收集操作【应用】

  • 概念

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

  • 常用方法

    方法名说明
    R collect(Collector collector)把结果收集到集合中
  • 工具类Collectors提供了具体的收集方式

    方法名说明
    public static <T> Collector toList()把元素收集到List集合中
    public static <T> Collector toSet()把元素收集到Set集合中
    public static Collector toMap(Function keyMapper,Function valueMapper)把元素收集到Map集合中
  • 代码演示       

             //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);

3.方法引用

3.2方法引用符

  • 方法引用符

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

  • 推导与省略

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

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

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

3.3引用类方法【应用】

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

  • 格式

    类名::静态方法

  • 范例

    Integer::parseInt

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

3.4引用对象的实例方法【应用】

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

  • 格式

    对象::成员方法

  • 范例

    "HelloWorld"::toUpperCase

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

3.5引用类的实例方法【应用】

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

  • 格式

    类名::成员方法

  • 范例

    String::substring

    public String substring(int beginIndex,int endIndex)

    从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex

3.6引用构造器【应用】

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

  • 格式

    类名::new

  • 范例

    Student::new

  • 练习描述

    • 定义一个类(Student),里面有两个成员变量(name,age)

      并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法

    • 定义一个接口(StudentBuilder),里面定义一个抽象方法

      Student build(String name,int age);

    • 定义一个测试类(StudentDemo),在测试类中提供两个方法

      • 一个方法是:useStudentBuilder(StudentBuilder s)

      • 一个方法是主方法,在主方法中调用useStudentBuilder方法

     public class Student {
         private String name;
         private int age;
     ​
         public Student() {
         }
     ​
         public Student(String name, int age) {
             this.name = name;
             this.age = 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 interface StudentBuilder {
         Student build(String name,int age);
     }
     ​
     public class StudentDemo {
         public static void main(String[] args) {
     ​
             //Lambda简化写法
             useStudentBuilder((name,age) -> new Student(name,age));
     ​
             //引用构造器
             useStudentBuilder(Student::new);
     ​
         }
     ​
         private static void useStudentBuilder(StudentBuilder sb) {
             Student s = sb.build("林青霞", 30);
             System.out.println(s.getName() + "," + s.getAge());
         }
     }
  • 使用说明

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值