文章目录
- 一、创建不可变集合
 - 二、Stream流
 - 2.1 Stream流的获取
 - 2.1 Stream流的中间方法
 - 2.2 Stream流的终结方法
 
一、创建不可变集合
意义:如果一个集合中的数据在复制或使用过程中不能修改,或者被其他对象调用时不能改变内部数据,即增加数据的安全性。
不可变集合的创建
 针对每种类型的集合都有对应的创建方法如下表:
 
package com.itheima.jinjie10;
import java.util.*;
public class ImmutableCollection {
    public static void main(String[] args) {
        List<String> list = List.of("你好", "我好", "大家好");
        System.out.println(list.get(0));//获取元素
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }
        //set集合尝试
        Set<String> set = Set.of("你好", "我好", "大家好");
        for (String s : set) {
            System.out.println(s);
        }
//        set.remove()会报错
//        Map集合尝试
        HashMap<String, String> hsm = new HashMap<>();//之前的map创建方法,先创建后添加
        //不可变Map的创建
        Map<String, String> map = Map.of("asdf", "adsf", "aefds", "ads");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
        }
        //当集合中要存的参数大于10时
        HashMap<String, String> hsm1 = new HashMap<>();
        hsm1.put("aaa", "111");
        hsm1.put("bbb", "222");
        hsm1.put("ccc", "111");
        System.out.println(hsm1);
//         在JDK10以后有了简便的方法可直接由普通集合转为不可变集合
        Map<String, String> stringStringMap = Map.copyOf(hsm1);
        //老版本的集合转换
        Map<Object, Object> objectObjectMap2 = Map.ofEntries(hsm1.entrySet().toArray(new Map.Entry[0]));
        //先将集合的实体对转换为集合hsm1.entrySet()
        //由于 Map.Entry 是一个接口,你不能直接创建一个 Map.Entry[] 类型的数组。可以传递一个长度为0的“类型标记”数组(即 new Map.Entry[0]),
        //然后 toArray 方法会根据集合的大小和元素的类型来创建一个新的、正确类型的数组。
    }
}
 
注意:
- 不可变的Set集合中的值应该是不可重复的
 - Map.of方法的参数传递是有上限的,最多只能传递20个参数共10个键值对,故针对该情况可以使用JDK10后的copyof方法
 
二、Stream流
Stream流(也叫Stream API)。它是从JDK8以后才有的一个新特性,是专业用于对集合或者数组进行便捷操作的。
Stream流类似于流水线,将数组或集合中的数据进行多层过滤,转换等最后得到结果
使用步骤:
- 获取一条Stream流
 - 使用中间方法(过滤、转换)对数据进行操作
 - 使用终结方法(统计、打印)对数据进行操作
 
2.1 Stream流的获取

package com.itheima.jinjie10;
import java.util.*;
import java.util.stream.Stream;
public class StreamTest {/**
 * 目标:掌握Stream流的创建。
 */
    public static void main(String[] args) {
        // 1、如何获取List集合的Stream流?
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");
        //申请一个针对集合的对象
        Stream<String> stream = names.stream();
        // 2、如何获取Set集合的Stream流?
        Set<String> set = new HashSet<>();
        Collections.addAll(set, "刘德华","张曼玉","蜘蛛精","马德","德玛西亚");
        Stream<String> stream1 = set.stream();
        //用stream对象先筛选包含”德"的字符串,在用foreach方法遍历
        stream1.filter(s -> s.contains("德")).forEach(s -> System.out.println(s));
        // 3、如何获取Map集合的Stream流?
        Map<String, Double> map = new HashMap<>();
        map.put("古力娜扎", 172.3);
        map.put("迪丽热巴", 168.3);
        map.put("马尔扎哈", 166.3);
        map.put("卡尔扎巴", 168.3);
        //注意:双列集合不能直接转化为Stream流,需要将键与值分别转换为两个集合从而生成Stream流
        Set<String> keys = map.keySet();
        Stream<String> ks = keys.stream();
        Collection<Double> values = map.values();
        Stream<Double> vs = values.stream();
        //方法2:用实体集合来生成流
        Set<Map.Entry<String, Double>> entries = map.entrySet();
        Stream<Map.Entry<String, Double>> kvs = entries.stream();
        kvs.filter(e -> e.getKey().contains("巴"))
                .forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue()));
        // 4、如何获取数组的Stream流?
        String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"};
        Stream<String> s1 = Arrays.stream(names2);
        //5、针对零散的数据
        //在创建时只需要保证数据类型的一致性,一般都是针对引用类型的零散数据
        Stream.of("1","1","1","5","1","1").forEach(s -> System.out.println(s));
    }
}
 
注意:
- Map集合没有对应的流可供直接使用
 - 上述代码中的过滤器为一种函数式接口,类似于foreach方法可供直接使用
 - 用流处理零散数据一般针对引用型数据
 
2.1 Stream流的中间方法
中间方法是为了流式编程所需的必要步骤,主要用于数据的处理。
中间方法主要有以下部分:
 
package com.itheima.jinjie10;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class StreamMid {
    /**
     * 目标:掌握Stream流提供的常见中间方法。
     */
        public static void main(String[] args) {
            List<Double> scores = new ArrayList<>();
            Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);
            // 需求1:找出成绩大于等于60分的数据,并升序后,再输出。
            //此处可以用到过滤方法filter进行筛选,filter函数本身的参数也是一个匿名内部类
            //在过滤器中返回值为真的表示要保留的内容
            scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s));
            List<Student> students = new ArrayList<>();
            Student s1 = new Student("蜘蛛精", 26, 172.5);
            Student s2 = new Student("蜘蛛精", 26, 172.5);
            Student s3 = new Student("紫霞", 23, 167.6);
            Student s4 = new Student("白晶晶", 25, 169.0);
            Student s5 = new Student("牛魔王", 35, 183.3);
            Student s6 = new Student("牛夫人", 34, 168.5);
            Collections.addAll(students, s1, s2, s3, s4, s5, s6);
            // 需求2:找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出.
            students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30)
                    .sorted((o1, o2) -> (int) (o2.getAge() - o1.getAge()))
                    .forEach(s -> System.out.println(s));
            // 需求3:取出身高最高的前3名学生,并输出。
            //limit方法一般与skip合用,用limit获取前几个数据控制数据的结尾处,在用skip将前段不需要的部分去除控制数据的起始位置
            students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                    .limit(3).forEach(System.out::println);
            System.out.println("-----------------------------------------------");
            // 需求4:取出身高倒数的2名学生,并输出。   s1 s2 s3 s4 s5 s6
            students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                    .skip(students.size() - 2).forEach(System.out::println);
            // 需求5:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。
//            用map方法可以转换流中存储数据的类型,其中的函数式接口的前一个泛型为流中存储的数据类型,后一个为要转换为的数据类型
//            students.stream().map(new Function<Student, String>() {
//                @Override
//                public String apply(Student student) {
//
//                    Student.toString();
//
//                    return null;
//                }
//            })
            students.stream().filter(s -> s.getHeight() > 168).map(Student::getName)
                    .distinct().forEach(System.out::println);
            // distinct去重复,自定义类型的对象(希望内容一样就认为重复,重写hashCode,equals)
            students.stream().filter(s -> s.getHeight() > 168)
                    .distinct().forEach(System.out::println);
            Stream<String> st1 = Stream.of("张三", "李四");
            Stream<String> st2 = Stream.of("张三2", "李四2", "王五");
            Stream<String> allSt = Stream.concat(st1, st2);
            allSt.forEach(s->System.out.println(s));
        }
}
 
注意:
- 中间方法返回的是Stream流原来的流只能使用一次,所以一般不会为其专门创建一个新对象而是采用链式编程的思路编写
 - 修改Stream流中的数据不会改变原来的数据
 - 一般不用中间方法进行结尾
 - 在用concat方法进行合并时应尽可能保证流中的数据一致
 
2.2 Stream流的终结方法
常见的终结方法都是对流数据的一个汇总

 从源码中可见终结方法与中间方法的显著区别之一,中间方法返回的任然是一个流类型的数据,而终结方法不返回或返回其他类型的数据
 
 使用方法举例
public class CollectFun {
    public static void main(String[] args) {
        ArrayList<String> list =new ArrayList<>();
        Collections.addAll(list,"你好-男","我好-男","大家好-男","你好-女","我好-女","大家好-女","大家好-男");
        //将符合要求的数据收集到list集合中
        List<String> collectlis = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toList());
        System.out.println(collectlis);
        //将符合要求的数据收集到Set集合中,集合中会去重
        Set<String> collectset = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toSet());
        System.out.println(collectset);
        //将符合要求的数据收集到Map集合中
        /*
        * toMap有两个参数:分别是键的和值的生成规则
        *
        * 每个生成规则中有两个泛型,前一个表示流中的数据类型,后一个表示要生成的数据类型
        *
        * 方法apply中的形参表示流中传入的每一个数据,在方法体中是生成键的代码,返回值类型是键或值的类型,返回值是已经生成的键或值
        *
        * */
        Map<String,String> collectmap = list.stream().filter(s -> "男".equals(s.split("-")[1])).
                collect(Collectors.toMap(new Function<String, String>() {//第一个参数表示流中的数据类型,第二个为键的数据类型
            @Override
            public String apply(String s) {
                return s.split("-")[0];
            }
        },s-> s.split("-")[1] ));
        //注意:tomap时键是不能重复的,上述代码就会报错
    }
}
                


















