十七、JDK8 新特性
17.1 Lambda表达式介绍
17.1.1 Lambda表达式案例
先来看案例:
package com.itheima.lambda;
/**
 * @author: Carl Zhang
 * @create: 2021-12-31 14:57
 * 体验Lambda表达式和传统表达式区别
 * 实作游泳
 */
public class Lambda01 {
    public static void main(String[] args) {
        /*
        * 结论:1. 传统的匿名内部类方法解决界面自变量问题,需要 创建匿名内部类物件,实作界面方法,两步 --关注点 怎么做
        *      2. Lambda表达式只需要一条式子,代码简介,关注点更明确在方法功能和输出 -- 关注点 做什么
        *      3. 这种关注方法能做什么的思想就是函式式编程思想
        * */
        //传统方法实作游泳 - 匿名内部类
        swim(new Swim() {
            @Override
            public void swimming() {
                System.out.println("匿名内部类的游泳....");
            }
        });
        //Lambda表达式实作
        swim(() -> System.out.println("匿名内部类的游泳...."));
    }
    public static void swim (Swim swim) {
        swim.swimming();
    }
}
interface Swim {
    /**
     * 游泳
     * */
    void swimming();
}
注意:lambda 表达式可以理解为对匿名内部类的一种简化 , 但是本质是有区别的
17.1.2 引入函式式编程思想
介绍:
- 在数学中,函式就是有输入量、输出量的一套计算方案,也就是“拿资料做操作”
- 而 lambda是就是函式式编程思想的一种体现
17.1.3 函式式编程思想和面向物件编程思想的对比
- 面向物件思想 :
- 强调的是用物件去完成某些功能 -- 怎么做
 
- 函式式编程思想 :
- 强调的是结果 , 而不是怎么去做 -- 做什么
 
17.2 函式式界面
17.2.1 函式式界面介绍
- 
概念: - 只有一个抽象方法需要重写的界面就是函式式界面,
- 函式式界面是允许有其他的非抽象方法的存在例如静态方法,默认方法,私有方法,
 
- 
注解: 为了标识界面是一个函式式界面,可以在界面之上加上一个注解: @FunctionalInterface
- 
相关API : JDK中的java.util.function包里的界面都是函式式界面
我们以前学的 Runnable 界面也是函式式界面
.png)
也可以自定义一个函式式界面:
package com.itheima.lambda;
/**
 * @author CarlZhang
 * 自定义一个函式式界面
 */
@SuppressWarnings("ALL")
@FunctionalInterface
public interface FunctionalInterface01 {
    /**
     * 只有一个 要重写的 抽象方法
     */
    void method01();
    /**
     * 继承Object类的方法
     */
    @Override
    String toString();
    
    /**
     * jdk1.8 界面里可以有静态方法和默认方法
     * */
    static void method02() {
        System.out.println("FunctionalInterface01界面里的静态方法");
    }
    
    /**
     * 默认方法
     * */
    default void method03() {
        System.out.println("FunctionalInterface01界面里的默认方法");
    }
}
@FunctionalInterface
interface FunctionalInterface02 extends FunctionalInterface01{
    //报错:因为此处有两个要重写的抽象法, 一个父类界面的一个此界面的,
    //结论:函式式界面里只能有一个要重写的抽象方法,父类界面里的也算,而Object类比较特殊,
    //      界面里有重写Object类的抽象方法不影响函式式界面的判定
    //void method02();
}
17.2.2 注意事项和使用细节
- 界面里只能有一个要重写的抽象方法,继承自 **Object**类的方法除外
- 可以用注解  @FunctionalInterface来表示函式式界面
17.3 Lambda表达式的使用
17.3.1 Lambda表达式语法
标准格式 :(形参串列) - > { //要实作方法的方法体... }
17.3.2 Lambda表达式使用案例
/**
 * @author: Carl Zhang
 * @create: 2021-12-31 16:32
 * 练习1:
 * 1 撰写一个界面(ShowHandler)
 * 2 在该界面中存在一个抽象方法(show),该方法是无自变量无回传值
 * 3 在测验类(ShowHandlerDemo)中存在一个方法(useShowHandler)
 *   方法的的自变量是ShowHandler型别的,在方法内部呼叫了ShowHandler的show方法
 */
public class ShowHandlerDemo {
    public static void useShowHandler(ShowHandler showHandler) {
        showHandler.show();
    }
    public static void main(String[] args) {
        //呼叫useShowHandler方法
        //使用Lambda表达式实作ShowHandler界面作为自变量
        useShowHandler(() -> {
            System.out.println("使用Lambda表达式实作ShowHandler界面作为自变量");
        });
    }
    /*
    * Lambda表达式格式决议
    * 1. () 表示实作的界面里方法的形参串列
    * 2. -> 语法规定,指向要实作的方法内容
    * 3. {} 要实作的方法的方法体
    *
    * 注意:Lambda表达式实作的界面必须是函式式界面
    * */
}
/**
 * @author CarlZhang
 */
@FunctionalInterface
public interface ShowHandler {
    /**
     * 在该界面中存在一个抽象方法(show),该方法是无自变量无回传值
     * */
    void show();
}
/**
 * @author: Carl Zhang
 * @create: 2021-12-31 16:48
 * 需求
 * 1 首先存在一个界面(StringHandler)
 * 2 在该界面中存在一个抽象方法(printMessage),该方法是有自变量无回传值
 * 3 在测验类(StringHandlerDemo)中存在一个方法(useStringHandler),
 *   方法的的自变量是StringHandler型别的,
 *   在方法内部呼叫了StringHandler的printMessage方法
 */
public class StringHandlerDemo {
    public static void useStringHandler(StringHandler stringHandler) {
        stringHandler.printMessage("Hello, World");
    }
    public static void main(String[] args) {
        //使用lambda表达式实作StringHandler, 作为自变量传递
        //结论:
        // 1. () 里的内容对应界面里方法()的内容,是形式自变量,lambda表达式看作一个界面实作类
        // 2. 只有一个自变量情况下,可以省略() 
        useStringHandler(String s -> {
            System.out.println("呼叫Lambda表达式的代码块 " + s);
        });
        //匿名内部类的方式实作
        useStringHandler(new StringHandler() {
            @Override
            public void printMessage(String s) {
                System.out.println("匿名内部类的方法 " + s);
            }
        });
    }
}
@FunctionalInterface
public interface StringHandler {
    /**
     * 在该界面中存在一个抽象方法(printMessage),该方法是有自变量无回传值
     * @param s 任意字符串
     */
    void printMessage(String s);
}
package com.heima.lambda;
import org.omg.CORBA.PUBLIC_MEMBER;
/**
 * @author Carl Zhang
 * @description
 * @date 2022/1/1 20:32
 * 1 首先存在一个界面(Calculator)
 * 2 在该界面中存在一个抽象方法(calc),该方法是有自变量也有回传值
 * 3 在测验类(CalculatorDemo)中存在一个方法(useCalculator)
 * 方法的的自变量是Calculator型别的
 * 在方法内部呼叫了Calculator的calc方法
 */
public class CalculatorDemo {
    public static void useCalculator(Calculator calculator) {
        System.out.println(calculator.calc(11, 12));
    }
    public static void main(String[] args) {
        /*
         * 1. 有参有回传值的方法,直接写(形参串列) -> { return 回传值; },
         *    进一步体现 (输入) - > {输出} 的函式式编程思想
         * 2. 自变量型别可以省略,有多个自变量不能只省略一个
         * 3. 代码块只有一句,则可以省略大括号和分号,甚至return
         * */
        //useCalculator((int num1, int num2) -> {
        //    return num1 + num2;
        //});
        useCalculator((num1, num2) -> num1 + num2);
    }
}
/**
 * @author CarlZhang
 * 1 首先存在一个界面(Calculator)
 * 2 在该界面中存在一个抽象方法(calc),该方法是有自变量也有回传值
 */
@FunctionalInterface
public interface Calculator {
    /**
     * 计算两数之和
     * @param num1 第一个数
     * @param num2 第二个数
     * @return 两数之和
     */
    int calc(int num1, int num2);
}
17.3.2 注意事项和使用细节
使用前提 :Lambda 表达式实作的界面必须是函式式界面
格式决议:
- Lambda表达式可看做函式式界面的一个实作类物件
- ()表示实作的界面里方法的形参串列,没有可以空着,- 自变量型别可以省略,有多个自变量不能只省略一个
- 只有一个自变量可以省略 ()
 
- ->语法规定,指向要实作的方法内容
- {}要实作的方法的方法体- 代码块里只有一句,则可以省略 {}和;,甚至return
 
- 代码块里只有一句,则可以省略 
- (形参) -> {回传值}的格式体现了- (输入) -> {输出}的函式式编程思想
17.4 Lambda表达式和匿名内部类的区别
- 作用物件不同 :
- 匿名内部类:可以是界面,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是函式式界面
 
- 使用场景不同 :
- 如果界面中有且仅有一个抽象方法,可以使用 Lambda表达式,也可以使用匿名内部类
- 如果界面中多于一个抽象方法,只能使用匿名内部类,而不能使用 Lambda表达式
 
- 如果界面中有且仅有一个抽象方法,可以使用 
- 实作原理不同 :
- 匿名内部类:编译之后,产生一个单独的 .class字节码档案
- Lambda表达式:编译之后,没有一个单独的- .class字节码档案,对应的字节码会在运行的时候动态生成
 
- 匿名内部类:编译之后,产生一个单独的 
/**
 * @author Carl Zhang
 * @description Lambda表达式和匿名内部类的区别
 * @date 2022/1/1 21:36
 */
public class LambdaVsAnonymous {
    public static void main(String[] args) {
        
        //Lambda表达式呼叫show方法 -- 编译后没有.class档案
        Test test = () -> System.out.println("Hello, World");
        test.show();
        //匿名内部类呼叫show方法 -- 有LambdaVsAnonymous$1.class档案
        new Test() {
            @Override
            public void show() {
                System.out.println("Hello, World");
            }
        }.show();
    }
}
@FunctionalInterface
interface Test {
    /**
     * 打印方法
     */
    void show();
}
17.5 Stream 流
17.5.1 Stream的体验
import java.util.ArrayList;
/**
 * @author Carl Zhang
 * @description 体验Stream流的好处
 * @date 2022/1/2 17:28
 * 需求:按照下面的要求完成集合的创建和遍历
 * <p>
 * 1 创建一个集合,存盘多个字符串元素
 * "张无忌" , "张翠山" , "张三丰" , "谢广坤" , "赵四" , "刘能" , "小沈阳" , "张良"
 * 2 把集合中所有以"张"开头的元素存盘到一个新的集合
 * 3 把"张"开头的集合中的长度为3的元素存盘到一个新的集合
 * 4 遍历上一步得到的集合
 */
public class Stream01 {
    public static void main(String[] args) {
        //集合的方式
        //1.创建集合,添加资料
        ArrayList<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("张翠山");
        list.add("张三丰");
        list.add("谢广坤");
        list.add("赵四");
        list.add("刘能");
        list.add("小沈阳");
        list.add("张良");
        ArrayList<String> newList = new ArrayList<>();
        ArrayList<String> newList2 = new ArrayList<>();
        //"张"开头的元素添加到新集合
        for (String s : list) {
            if (s.startsWith("张")) {
                //3."张"开头的元素添加到新集合
                newList.add(s);
            }
        }
        //"张"开头的且长度为3的添加到另一个元素
        for (String s : newList) {
            if (s.startsWith("张") && s.length() == 3) {
                newList2.add(s);
            }
        }
        //4.打印
        System.out.println(newList);
        System.out.println(newList2);
        System.out.println("===================");
        //Stream流的方式 获取并列印"张"开头的且长度为3的元素 -- 使对容器里资料的操作进行了简化
        list.stream().filter(s -> s.startsWith("张") && s.length()
                == 3).forEach(s -> System.out.println(s));
    }
}
17.5.2 Stream流介绍
.png)
.png)
17.6 Stream流三类方法
17.6.1 Stream流三类方法介绍
- 获取 Stream流- 创建一条流水线,并把资料放到流水线上准备进行操作
 
- 中间方法
- 流水线上的操作,
- 一次操作完毕之后,还可以继续进行其他操作
 
- 终结方法
- 一个 Stream流只能有一个终结方法
- 是流水线上的最后一个操作
 
- 一个 
17.6.2 Stream流 - 获取方法
- 单列集合
- 可以使用Collection界面中的默认方法stream()生成流
- default Stream<E> stream()
 
- 可以使用
- 双列集合
- 双列集合不能直接获取 , 需要间接的生成流
- 可以先通过 keySet() 或者entrySet()获取一个Set集合,再获取Stream流
 
- 阵列
- Arrays中的静态方法- stream生成流
 
import java.util.*;
import java.util.stream.Stream;
/**
 * @author Carl Zhang
 * @description Stream流的获取方法
 * @date 2022/1/2 17:59
 */
@SuppressWarnings("ALL")
public class StreamGetMethod {
    public static void main(String[] args) {
        //获取单列集合的Stream流
        singleSetStream();
        //获取双列集合的Stream流
        doubleSetStream();
        //获取阵列的Stream流
        arrayStream();
        //获取任意元素的stream流 --了解
        int[] array = {1, 2, 3, 4, 5, 6};
        Stream.of(array).forEach(i -> System.out.println(i)); //[I@7ba4f24f
        Stream.of(1, 2, 3, 4, 5, 6).forEach(i -> System.out.println(i));
    }
    private static void arrayStream() {
        System.out.println("获取阵列的Stream流");
        int[] arr = {1, 2, 3, 4, 5, 6};
        Arrays.stream(arr).forEach(i -> System.out.println(i));
    }
    private static void doubleSetStream() {
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("it001", "曹植");
        hashMap.put("it002", "曹丕");
        hashMap.put("it003", "曹熊");
        hashMap.put("it004", "曹冲");
        hashMap.put("it005", "曹昂");
        // 双列集合不能直接获取 , 需要间接的生成流
        // 可以先通过keySet或者entrySet获取一个Set集合,再获取Stream流
        System.out.println("获取双列集合的Stream流");
        Set<Map.Entry<String, String>> entries = hashMap.entrySet();
        entries.stream().forEach(entry -> System.out.println(entry.getKey() +
                "-" + entry.getValue()));
    }
    private static void singleSetStream() {
        ArrayList<String> list = new ArrayList<>();
        list.add("迪丽热巴");
        list.add("古力娜扎");
        list.add("马尔扎哈");
        list.add("欧阳娜娜");
        // 可以使用Collection界面中的默认方法stream()生成流
        // default Stream<E> stream()
        System.out.println("获取单列集合的Stream流");
        list.stream().forEach((String s) -> {
            System.out.println(s);
        });
    }
}
17.6.3 Stream流 - 中间方法
特点:回传了 Stream 流物件,用以继续呼叫方法进行操作流物件
- Stream<T> filter(Predicate predicate):用于对流中的资料进行过滤- Predicate界面中的方法 :- boolean test(T t):对给定的自变量进行判断,回传一个布林值
 
- 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<T> sorted (): 将流中元素按照自然排序的规则排序
- Stream<T> sorted (Comparator<? super T> comparator): 将流中元素按照自定义比较器规则排序
import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
 * @author Carl Zhang
 * @description Stream流的中间方法
 * @date 2022/1/2 19:12
 */
@SuppressWarnings("ALL")
public class StreamCentreMethod {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("张翠山");
        list.add("张三丰");
        list.add("谢广坤");
        list.add("赵四");
        list.add("刘能");
        list.add("小沈阳");
        list.add("张良");
        list.add("张良");
        list.add("张良");
        list.add(new String("张良"));
        //1 Stream<T> filter(Predicate predicate):用于对流中的资料进行过滤
        //  Predicate函式式界面的方法 : boolean test(T t):对给定的自变量进行判断,回传一个布林值
        //  T 是泛型,Stream流里的元素型别
        //  回传true就留下,false就过滤掉
        //打印集合中三个字名字的元素
        //list.stream().filter(new Predicate<String>() {
        //    @Override
        //    public boolean test(String s) {
        //        return s.length() == 3;
        //    }
        //}).forEach(s -> System.out.println(s));
        list.stream().filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
        //2 Stream<T> limit(long maxSize):截取指定自变量个数的资料
        //获取前两个元素
        Stream<String> stream = list.stream();
        stream.limit(2).forEach(s -> System.out.println(s));
        //3 Stream<T> skip(long n):跳过指定自变量个数的资料
        //跳过前两个元素,打印后面的元素
        //例外IllegalStateException:stream has already been operated upon or closed
        //stream.skip(2).forEach(s -> System.out.println(s));
        list.stream().skip(2).forEach(s -> System.out.println(s));
        //4 static <T> Stream<T> concat(Stream a, Stream b):合并a和b两个流为一个流
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("迪丽热巴");
        list2.add("古力娜扎");
        list2.add("欧阳娜娜");
        list2.add("马尔扎哈");
        Stream.concat(list.stream(), list2.stream()).forEach(s -> System.out.println(s));
        //5 Stream<T> distinct():去除流中重复的元素,依赖(hashCode()和equals())
        list.stream().distinct().forEach(s -> System.out.println(s));
        //这里只剩一个"张良",因为String里的方法根据value的值来回传hashCode
        //new String("张良").hashCode();
        //6 Stream<T> sorted () : 将流中元素按照自然排序的规则排序
        list.stream().sorted().forEach(s -> System.out.println(s));
        //7 Stream<T> sorted (Comparator<? super T> comparator) : 将流中元素按照自定义比较器规则排序
        list.stream().sorted(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.length() - o1.length();
            }
        }).forEach(s -> System.out.println(s));
        System.out.println();
        list.stream().sorted((s1, s2) -> s2.length() - s1.length()).forEach(
                s -> System.out.println(s)
        );
    }
}
17.6.4 Stream流 - 终结方法
特点:慷训传值,不能继续呼叫方法进行操作流物件
- void forEach(Consumer action):对此流的每个元素执行操作- Consumer界面中的方法- void accept(T t):对给定的自变量执行此操作
 
- long count():回传此流中的元素数
import java.util.ArrayList;
import java.util.function.Consumer;
/**
 * @author Carl Zhang
 * @description Stream流的终结方法
 * @date 2022/1/2 19:48
 */
public class StreamEndMethod {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("张翠山");
        list.add("张三丰");
        list.add("谢广坤");
        //1 void forEach(Consumer action):对此流的每个元素执行操作
        //  Consumer界面中的方法 void accept(T t):对给定的自变量执行此操作
        // 把list集合中的元素放在stream流中
        // forEach方法会回圈遍历流中的资料
        // 并回圈呼叫accept方法 , 把资料传给s
        // 所以s就代表的是流中的每一个资料
        // 我们只要在accept方法中对资料做业务逻辑处理即可
        list.stream().forEach(s -> System.out.println(s));
        //2. long count():回传此流中的元素数
        //结果:4
        System.out.println(list.stream().count());
    }
}
17.7 Stream流的收集方法
17.7.1 使用收集方法的原因
问题:使用 Stream 流的方式操作完毕之后,我想把流中的资料起来,该怎么办呢?
引出收集方法
package com.heima.stream;
import java.util.ArrayList;
/**
 * @author Carl Zhang
 * @description 使用收集方法的原因
 * @date 2022/1/2 20:04
 * 需求:过滤元素并遍历集合
 * 定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10
 * 将集合中的奇数洗掉,只保留偶数,
 * 遍历集合得到2,4,6,8,10
 */
public class CollectionMethod01 {
    public static void main(String[] args) {
        //JDK9 新特性 直接传入一个不可变集合的元素,来创建新集合
        //ArrayList<Integer> list = new ArrayList<>(List.of(1,2,3,4,5,6,7,8,9,10));
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        //使用Stream流过滤掉集合中的奇数,获取偶数
        list.stream().filter(i -> i % 2 == 0).forEach(i -> System.out.println(i));
        //结论:list集合里的数未改变
        //如果需要保留流里面过滤后的元素 -> 使用收集方法
        System.out.println(list);
    }
}
17.7.2 收集方法介绍
Stream 流的收集方法
R collect(Collector collector)  : 此方法只负责收集流中的资料 , 创建集合添加资料动作需要依赖于自变量
收集方法也可以看作一种终结方法,呼叫完 collect() 不回传 Stream 物件,不可再对流进行操作
17.7.2 三种收集方式
工具类 Collectors 提供了具体的收集方式
public static <T> Collector toList() :把元素收集到List集合中
public static <T> Collector toSet() :把元素收集到Set集合中
package com.heima.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
/**
 * @author Carl Zhang
 * @description Stream流的收集方法
 * @date 2022/1/2 20:22
 * 需求 :
 * 定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10
 * 将集合中的奇数洗掉,只保留偶数,
 * 遍历集合得到2,4,6,8,10
 */
public class CollectionMethod02 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        //通过收集方法获取过滤后的元素
        //决议:
        //1. R collect(Collector collector) : 此方法只负责收集流中的资料 , 创建集合添加资料动作需要依赖于自变量
        //2. 通过工具类Collector里的规则来将收集到的元素保存到集合里
        //   public static <T> Collector toList():把元素收集到List集合中
        List<Integer> list1 = list.stream().filter(i -> i % 2 == 0).collect(
                Collectors.toList());
        System.out.println(list1); //[2, 4, 6, 8, 10]
        //将过滤好的元素收集到Set集合里
        //public static <T> Collector toSet():把元素收集到Set集合中
        Set<Integer> set = list.stream().filter(i -> i % 2 == 0).collect(
                Collectors.toSet()
        );
        System.out.println(set); //[2, 4, 6, 8, 10]
    }
}
public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * @author Carl Zhang
 * @description 将过滤好的元素收集到Map集合里
 * @date 2022/1/2 20:33
 * public static  Collector toMap(Function keyMapper,Function valueMapper):
 * 把元素收集到Map集合中
 * <p>
 * 1 创建一个ArrayList集合,并添加以下字符串,字符串中前面是姓名,后面是年龄
 * "zhangsan,23"
 * "lisi,24"
 * "wangwu,25"
 * 2 保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
 */
public class CollectionMethod03 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("zhangsan,23");
        list.add("lisi,24");
        list.add("wangwu,25");
        //筛选出24岁以上的元素
        Stream<String> stream = list.stream().filter(s -> {
            String[] split = s.split(",");
            //获取年龄
            int age = Integer.parseInt(split[1]);
            //筛选出大于等于24岁的
            return age >= 24;
        });
        //将过滤好的元素收集到Map集合
        //public static  Collector toMap(Function keyMapper,Function valueMapper):
        Map<String, String> collect = stream.collect(Collectors.toMap(
                // 获取键:Function keyMapper -- 传入一个函式式界面的实作类
                // s 表示流里的元素
                // 获取第一个元素,做为键
                s -> s.split(",")[0],
                // 获取值Function valueMapper)
                s -> s.split(",")[1]
        ));
        //遍历
        Set<Map.Entry<String, String>> entries = collect.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }
    }
}

 
							 
										
										 
										
										 
										
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										 
										
										
0 评论