01.第一章：常用函数式接口_Predicate判断接口：
	1).java.util.function.Predicate(函数式接口)
	2).抽象方法：
		1).boolean test(T t):用于验证一个数据。
	3).默认方法：
		1).and():用于将两个条件"并且"
		2).or():用于将两个条件“或"
		3).negate():非
	4).示例代码：
public static void main(String[] args) {
    //1.抽象：boolean test(T t):
    //需求：判断字符串的长度是否是大于等于5，是：打印：很长，否则，打印：不长
    m1((s) -> s.length() >= 5, "ello");
    //需求：判断分数大于90，打印：高分，否则打印：非高分
    m2((n) -> n >= 90, 89);

    //2.默认：and()
    //需求：要求字符串是否即包含大写H，又包含大写的W
    m3((s) -> s.contains("H"), (s) -> s.contains("W"), "Helloworld");

    //3.默认：or()
    //需求：要求字符串是否包含大写H《或者》包含大写的W
    m4(s -> s.contains("H"),s -> s.contains("W"),"helloworld");

    //4.默认：negate()
    //需求：判断字符串是否不包含大写的H

    m5(s -> s.contains("H"), "Hello");

}
//1.抽象方法：boolean test(T t):
//需求：判断字符串的长度是否是大于等于5，是：打印：很长，否则，打印：不长
public static void m1(Predicate<String> p,String s){
    boolean b = p.test(s);
    System.out.println("字符串：" + s + (b? "很长" : "不长"));
}

//需求：判断分数大于90，打印：高分，否则打印：非高分
public static void m2(Predicate<Integer> p, int score) {
    boolean b = p.test(score);
    System.out.println("分数：" + score + (b ? " 高分" : " 不高分"));
}

//2.and()
//需求：要求字符串是否即包含大写H，又包含大写的W
public static void m3(Predicate<String> p1, Predicate<String> p2, String string) {
    boolean b = p1.and(p2).test(string);
    System.out.println("字符串是否即包含大写H，又包含大写的W：" + b);
}

//3.默认：or()
//需求：要求字符串是否包含大写H《或者》包含大写的W
public static  void m4(Predicate<String> p1,Predicate<String> p2,String s) {
    System.out.println("符串是否包含大写H《或者》包含大写的W：" + p1.or(p2).test(s));
}

//4.默认：negate()
//需求：判断字符串是否不包含大写的H
public static void m5(Predicate<String> p1,String s){
    System.out.println("判断字符串是否不包含大写的H：" + p1.negate().test(s));
}

02.第一章：常用函数式接口_Predicate判断接口_练习：
	1).题目：有以下数据：
		String[ ] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
		请将满足条件的数据存储到集合中，要满足以下条件：
		1).女生
		2).名字为四个字
	2).示例代码：

public class Demo {
    public static void main(String[] args) {
        /*
        String[ ] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
		请将满足条件的数据存储到集合中，要满足以下条件：
		1).女生
		2).名字为四个字

         */
        String[ ] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
        List<String> list = new ArrayList<>();
        for (String s : array) {
            boolean b = m1(str -> str.split(",")[1].equals("女"),
                            str -> str.split(",")[0].length() == 4,s);
            if (b == true) {
                list.add(s);
            }
        }
        System.out.println(list);
    }

    public static boolean m1(Predicate<String> p1, 
						Predicate<String> p2, String str) {
        return p1.and(p2).test(str);
    }
}

03.第一章：常用函数式接口_Function函数接口：
	1).java.util.function.Function(函数接口)
	2).抽象方法：
		1).R apply(T t):接收一个类型，返回一个类型数据。场景：将String类型数据转换为Integer.
	3).默认方法：
		1).andThen():将第一次转换的结果继续传给第二个Function函数进行继续处理
	4).示例代码：
public class Demo {
    public static void main(String[] args) {
        //1.R apply(T t):接收一个数据，返回一个数据
        //需求：接收一个字符串的年龄，并将其转换为int年龄，然后 + 10
        m1(s -> Integer.parseInt(s), "24");

        //2.默认：andThen():
        //需求：接收一个字符串的年龄，并将其转换为int年龄，然后 + 10
        m2(s -> Integer.parseInt(s),n -> n + 10,"35");
    }

    //1.R apply(T t):接收一个数据，返回一个数据
    //需求：接收一个字符串的年龄，并将其转换为int年龄，然后 + 10
    public static void m1(Function<String, Integer> f1, String string) {
        int age = f1.apply(string);
        System.out.println("转换后的年龄为：" + (age + 10));
    }

    //2.默认：andThen():
    //需求：接收一个字符串的年龄，并将其转换为int年龄，然后 + 10
    public static void m2(Function<String, Integer> f1, Function<Integer, Integer> f2, String age) {
        int a = f1.andThen(f2).apply(age);
        System.out.println("两次处理后的结果：" + a);
    }

}

04.第一章：常用函数式接口_Function函数接口_练习：
	1).题目：有以下字符串：
			String str = "赵丽颖, 20";
		要求：
		1 . 将字符串截取数字年龄部分，得到字符串；
		2. 将上一步的字符串转换成为int类型的数字；
		3. 将上一步的int数字累加100， 得到结果int数字。

	2).示例代码：
public class Demo {
    public static void main(String[] args) {
        /*
            	String str = "赵丽颖, 20";
		要求：
		1 . 将字符串截取数字年龄部分，得到字符串；
		2. 将上一步的字符串转换成为int类型的数字；
		3. 将上一步的int数字累加100， 得到结果int数字。

         */
        String str = "赵丽颖,20";
        m1(s -> s.split(",")[1],
                s -> Integer.parseInt(s),
                n -> n + 100, str);

    }
    public static void m1(Function<String,String> f1,
                            Function<String,Integer> f2,
                            Function<Integer,Integer> f3,String str){
        int a = f1.andThen(f2).andThen(f3).apply(str);
        System.out.println("结果为：" + a);

    }
}

05.第一章：常用函数式接口_总结_延迟方法与终结方法：
	1).延迟方法：通常是“默认方法”，它们都会此接口的引用，作用可以继续使用方发链调用：
		f1.andThen(f2).andThen(f3).apply(str);

	2).终结方法：通常是“抽象方法”，返回值不固定，所以不能使用方法链的形式继续调用此接
	   口的其它方法。

--------------------------------------------------------------
06.第二章：Stream流_Stream流进行集合过滤的效果演示：
	1).jdk8开始由于类库中增加了Lambda，使得语言更加灵活；在这个基础上，由增加了一种类--
	   Stream流类。
	2).它不是IO流。它是一种对集合操作的“工具类”。它类似于：“迭代器”，但与Lambda结
	   合，可以提供比“迭代器”更加灵活的方式遍历集合。
	3).效果演示：
public class Demo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>() ;
        list. add("张无忌") ;
        list. add("周芷若") ;
        list. add("赵敏") ;
        list. add("张强") ;
        list. add("张三丰") ;

        //需求：找出所有的张姓学员，并打印前两位
       /* List<String> stuList = new ArrayList<>();
        for (String s : list) {
            if(s.startsWith("张")){
                stuList.add(s);
            }
        }
        for (int i = 0; i < stuList.size() ; i++) {
            if (i == 0 || i == 1) {
                System.out.println(stuList.get(i));
            }

        }*/

        //使用Stream流的方式
        list.stream()
                .filter(s -> s.startsWith("张"))
                .limit(2).forEach(System.out::println);
    }
}

07.第二章：Stream流_流思想概述：
	1)."Stream流"它就是操作集合的“工具类”。
	2).尤其是对集合进行一系列的筛选、过滤、求个数....这时使用Stream流+Lambda表达式，会
	   非常的方便。
	3).类层次结构：
		AutoCloseable（接口）
			|--BaseStream(接口)
				|--DoubleStream(接口)
				|--LongStream(接口)
				|--IntStream(接口)
				|--Stream(接口)
					1).count():求流中元素的个数
					2).filter():过滤的方法
					3).forEach():获取每个元素的方法
					....
					
12.第二章：Stream流_获取List_Set_Map_数组流：
	1).对于List集合：
		List<String> list = new ArrayList<>();
		...
		Stream<String> stream = list.stream();
	2).对于Set集合：
		Set<String> set = new HashSet<>();
		...
		Stream<String> stream = set.stream();
	3).对于Map集合：
		Map<String,Integer> map = new HashMap<>();
		...
		Set<String> keys = map.keySet();
		Stream<String> stream = keys.stream();
	4).数组：
		1).零散的数据：
			Stream<Integer> intStream = Stream.of(10,20,30,40,50);
		2).传递一个数组：
			Integer[] arr = {1,43,2,432,52,432};//注：必须是引用类型数组；
			Stream<Integer> intStream = Stream.of(arr);

		扩展：对于一个int数组，long数组，double数组(是基本类型)
			int[] arr = {10,20,30};
			IntStream intStream = IntStream.of(arr);
			intStream中包含很多对int数组操作的方法：求最大值(max())，最小值(min()
		
13.第二章：Stream流_常用方法_过滤filter
	1).Stream<T> filter(Predicate<? super T> predicate);参数用于判断，
				  Predicate中的抽象方法：boolean test(T t)
	2).示例代码：
public static void main(String[] args) {
    List<String> list = new ArrayList<>() ;
    list. add("张无忌") ;
    list. add("周芷若") ;
    list. add("赵敏") ;
    list. add("张强") ;
    list. add("张三丰") ;

    list.stream()
		.filter(s -> s.startsWith("张"))
		.filter(s -> s.length() == 2);

}

14.第二章：Stream流_常用方法_逐一处理forEach
public static void main(String[] args) {
    List<String> list = new ArrayList<>() ;
    list. add("张无忌") ;
    list. add("周芷若") ;
    list. add("赵敏") ;
    list. add("张强") ;
    list. add("张三丰") ;

    List<String> list2 = new ArrayList<>();
    list.stream()
            .filter(s -> s.startsWith("张"))
            .filter(s -> s.length() == 2)
            .forEach((s) -> list2.add(s));
    System.out.println(list2);
}

15.第二章：Stream流_常用方法_统计个数conte
public static void main(String[] args) {
    List<String> list = new ArrayList<>() ;
    list. add("张无忌") ;
    list. add("周芷若") ;
    list. add("赵敏") ;
    list. add("张强") ;
    list. add("张三丰") ;

    List<String> list2 = new ArrayList<>();
   long c =  list.stream()
            .filter(s -> s.startsWith("张"))
            .filter(s -> s.length() == 2)
            .count();
    System.out.println("张姓的，2个字的人共有：" + c + " 个");
}

16.第二章：Stream流_常用方法_取用前几个limit
public static void main(String[] args) {
    List<String> list = new ArrayList<>() ;
    list. add("张无忌") ;
    list. add("周芷若") ;
    list. add("赵敏") ;
    list. add("张强") ;
    list. add("张三丰") ;

    List<String> list2 = new ArrayList<>();
    list.stream()
            .filter(s -> s.startsWith("张"))
            .limit(2)
            .forEach(System.out::println);

}

17.第二章：Stream流_常用方法_跳过前几个skip
public static void main(String[] args) {
    List<String> list = new ArrayList<>() ;
    list. add("张无忌") ;
    list. add("周芷若") ;
    list. add("赵敏") ;
    list. add("张强") ;
    list. add("张三丰") ;

    List<String> list2 = new ArrayList<>();
    list.stream()
            .filter(s -> s.startsWith("张"))
            .skip(2)
            .forEach(System.out::println);
}

18.第二章：Stream流_常用方法_映射（转换）map
		以下示例将一个Steram<String>转换为Stream<Integer>
   public static void main(String[] args) {
       
        //map转换方法
        Stream<String> s1 = Stream.of("10","20","30","40");
//        Stream<Integer> s2 = s1.map((s)->Integer.parseInt(s));
        Stream<Integer> s2 = s1.map(Integer::parseInt);
    }

19.第二章：Stream流_常用方法_合并concat
Stream<String> r1 = Stream.of("10","20","30");
Stream<String> r2 = Stream.of("40","50","60");
Stream<String> rAll = Stream.concat(r1,r2);
rAll.forEach(System.out::println);
20.第二章：Stream流_练习_集合元素处理的传统方式
		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("洪七公") ;
    List<String> two = new ArrayList<>() ;
    two. add("古力娜扎") ;
    two. add("张无忌") ;
    two. add("张三丰") ;
    two. add("赵丽颖") ;
    two. add("张二狗") ;
    two. add("张天爱") ;
    two. add("张三") ;
    /*
    1 .第一个队伍只要名字为3个字的成员姓名；
    2. 第一个队伍筛选之后只要前3个人；
    3. 第二个队伍只要姓张的成员姓名；
    4. 第二个队伍筛选之后不要前2个人；
    5. 将两个队伍合并为一个队伍；
    6. 根据姓名创建 Person 对象；
    7. 打印整个队伍的Person对象信息。
     */
    // 1 .第一个队伍只要名字为3个字的成员姓名；
    List<String> one1 = new ArrayList<>();
    for (String s : one) {
        if (s.length() == 3) {
            one1.add(s);
        }
    }
    //2. 第一个队伍筛选之后只要前3个人；
    List<String> one2 = new ArrayList<>();
    for (int i = 0; i <3 ; i++) {
        one2.add(one1.get(i));
    }

    //3. 第二个队伍只要姓张的成员姓名；
    List<String> two1 = new ArrayList<>();
    for (String s : two) {
        if (s.startsWith("张")) {
            two1.add(s);
        }
    }
    // 4. 第二个队伍筛选之后不要前2个人；
    List<String> two2 = new ArrayList<>();
    for (int i = 2; i < two1.size(); i++) {
        two2.add(two1.get(i));
    }

    //5. 将两个队伍合并为一个队伍；
    List<String> listAll = new ArrayList<>();
    listAll.addAll(one2);
    listAll.addAll(two2);

    //6. 根据姓名创建 Person 对象；
    List<Person> personList = new ArrayList<>();
    for (int i = 0; i < listAll.size(); i++) {
        personList.add(new Person(listAll.get(i)));
    }

    //7. 打印整个队伍的Person对象信息。
    for (Person p : personList) {
        System.out.println(p);
    }
}

21.第二章：Stream流_练习_集合元素处理的Stream方式：
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("洪七公") ;
    List<String> two = new ArrayList<>() ;
    two. add("古力娜扎") ;
    two. add("张无忌") ;
    two. add("张三丰") ;
    two. add("赵丽颖") ;
    two. add("张二狗") ;
    two. add("张天爱") ;
    two. add("张三") ;
    /*
    1 .第一个队伍只要名字为3个字的成员姓名；
    2. 第一个队伍筛选之后只要前3个人
    3. 第二个队伍只要姓张的成员姓名；
    4. 第二个队伍筛选之后不要前2个人；
    5. 将两个队伍合并为一个队伍；
    6. 根据姓名创建 Person 对象；
    7. 打印整个队伍的Person对象信息。
     */
    //1 .第一个队伍只要名字为3个字的成员姓名；2. 第一个队伍筛选之后只要前3个人；
    Stream<String> s1 = one.stream().filter(s -> s.length() == 3).limit(3);

    //3. 第二个队伍只要姓张的成员姓名；4. 第二个队伍筛选之后不要前2个人；
    Stream<String> s2 = two.stream().filter(s -> s.startsWith("张")).skip(2);

    //5. 将两个队伍合并为一个队伍；
    Stream.concat(s1,s2).map(Person::new).forEach(System.out::println);
}

22.第二章：Stream流_总结_拼接方法与终结方法：
	1).拼接方法(延迟方法):默认方法。它会返回此接口类型，所以可以链式编程
	2).终结方法：抽象方法。

23.第二章：Stream流_常用方法_并发流parallel方法:
	1).并发流：会针对流的每个操作(筛选、过滤、求数量)都单独开启一个线程去执行；
	2).示例代码：
public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 50000000; i++) {
        list.add(i);

    }

    System.out.println("封装结束，开始查找：");
    new Thread(()->{
        long start = System.currentTimeMillis();
        long c= list.stream()
                .filter(n -> n % 2 == 0)
                .filter(n -> n % 4 == 0)
                .filter(n -> n % 5 == 0)
                .filter(n -> n % 3 == 0)
                .count();
        long end = System.currentTimeMillis();

        System.out.println("普通Stream流 时间：" + (end - start) + " 毫秒 ，数量：" + c + " 个");
    }).start();
    new Thread(()->{
        long start = System.currentTimeMillis();
        long c= list.stream().parallel()
                .filter(n -> n % 2 == 0)
                .filter(n -> n % 4 == 0)
                .filter(n -> n % 5 == 0)
                .filter(n -> n % 3 == 0)
                .count();
        long end = System.currentTimeMillis();

        System.out.println("并发Stream流 时间：" + (end - start) + " 毫秒 ，数量：" + c + " 个");
    }).start();
}

24.第二章：Stream流_常用方法_收集Stream结果collect方法
	1).收集Stream结果：将Stream中的数据转换为List、Set集合、数组
	2).转换为List集合：
		Stream<String> s1 = Stream.of("a","b","c","d");
		List<String> list = s1.collect(Collectors.toList());
	   转换为Set集合
		Set<String> set = s1.collect(Collectors.toSet());
	   转换为数组：
		Object[] objArray = s1.toArray();

25.第二章：Stream流_练习_将数组元素添加到集合中：
public static void main(String[] args) {
    String[ ] array = { "Java", "Groovy", "Scala", "Kotlin" };
    List<String> list = Stream.of(array).collect(Collectors.toList());
    for (String s : list) {
        System.out.println(s);
    }

}

------------------------------------------------------------
学习目标总结：
01.能够使用Function函数式接口
	1).抽象方法：
		R apply(T ):接收一个，返回一个，常用于转换；
	2).默认方法：
		andThen():用于两个转换连续操作。

02.能够使用Predicate函数式接口
	1).抽象方法：
		boolean test(T t):
	2).默认方法：
		1).and:两个条件的并且；
		2).or:两个条件的或者
		3).negate:非
	
03.能够理解流与集合相比的优点
	1).流操作集合更灵活，更方便；

04.能够理解流的延迟执行特点
	1).可以继续方法链编程。
05.能够通过集合、 映射或数组获取流
	1).List获取流：
		list.stream();
	2).Set获取流：
		set.stream();
	3).Map获取流 ：
		Set<..> keys = map.keySet();
		Stream<...> stream = keys.stream();
06.能够掌握常用的流操作
	1).filter:过滤；
	2).limit:取前几个；
	3).skip：跳过几个；
	4).map : 转换
	5).count:求流中元素的个数；
	6).concat : 合并两个流；
	7).forEach:逐一获取每个元素；
	8).parallel : 获取并行流；高效；
	9).collect : 将流中的元素转换为集合；
	10).toArray:将流中的元素转换为数组；

07.能够使用流进行并发操作
	List<..> list = new ArrayList<>();
	list.stream().parallel().filter(...)....
08.能够将流中的内容收集到集合中
	1).收集到List中
		...
		List<String> list = stream.collect(Collectors.toList());
	2).收集到Set中
		...
		Set<String> set = stream.collect(Collectors.toSet());
		...
	
09.能够将流中的内容收集到数组中
		...
		Object[] objArray = stream.toArray();
		....