part1. jdk8新特性详解

1.Lambda

//1.匿名内部类
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("新线程中执行的代码:"+Thread.currentThread().getName());
				
			}
		}).start();
		System.out.println("主线程中的代码:"+Thread.currentThread().getName());
		

Lambda表达式是一个匿名函数,可以理解为一段代码

new Thread(()->{
			System.out.println("新线程Lambda表达式。。。");
		}).start();

优点:简化了匿名内部类的使用,语法更加简洁;匿名内部类语法冗余;

Lambda语法规则:省去了面向对象的条条框框,Lambda标准格式组成:

(参数类型 参数名称 )->{

代码体;

}

格式说明:

  • (参数类型 参数名称 ):参数列表
  • {代码体;} :方法体
  • -> :箭头,分割参数列表和方法体

1.1 无参,无返回值lambda

定义接口

public interface UserService {
void show();
}

然后创建主方法使用

package com.newfeature;

import com.newfeature.service.UserService;

public class Demo02Lambda {

	public static void main(String[] args) {
		goShow(new UserService() {

			@Override
			public void show() {
				// TODO Auto-generated method stub
				System.out.println("执行show()方法");
			}
		});

		System.out.println("------------------");

		// 无参,无返回值lambda表达式
		goShow(() -> {
			System.out.println("Lamda show方法执行了。。");
		});

	}

	public static void goShow(UserService userService) {
		userService.show();
	}
}

输出

执行show()方法
------------------
Lamda show方法执行了。。

1.2 有参有返回值的lambda表达式

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
	private String name;
    private Integer age;
    private Integer height;
}

list集合保存多个person对象,然后对这些对象做根据age排序操作

package com.newfeature;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.newfeature.domain.Person;

public class Demo03Lambda {

	public static void main(String[] args) {
		List<Person> list=new ArrayList<>();
		list.add(new Person("zhouzhou",33,175));
		list.add(new Person("zhouzhou2",23,155));
		list.add(new Person("zhouzhou3",53,125));
		list.add(new Person("zhouzhou4",63,165));
		Collections.sort(list, new Comparator<Person>() {
			@Override
			public int compare(Person o1, Person o2) {
				return o1.getAge()-o2.getAge();
			}
		});
		for (Person person : list) {
			System.out.println(person);
		}
	}

	
}

lambda表达式实现排序

package com.newfeature;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.newfeature.domain.Person;

public class Demo03Lambda {

	public static void main(String[] args) {
		List<Person> list=new ArrayList<>();
		list.add(new Person("zhouzhou",33,175));
		list.add(new Person("zhouzhou2",23,155));
		list.add(new Person("zhouzhou3",53,125));
		list.add(new Person("zhouzhou4",63,165));
		

		Collections.sort(list,(Person o1,Person o2)->{
			return o1.getAge() - o2.getAge();
		});
		for (Person person : list) {
			System.out.println(person);
		}
	}

	
}

输出结果:

Person(name=zhouzhou2, age=23, height=155)
Person(name=zhouzhou, age=33, height=175)
Person(name=zhouzhou3, age=53, height=125)
Person(name=zhouzhou4, age=63, height=165)

1.3 @FunctionalInterface 注解

/**
 * @FunctionalInterface
 * 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
 */
@FunctionalInterface
public interface UserService {
void show();
}

1.4 lambda表达式的原理

  • 匿名内部类的本质是在编译时生产一个class文件,xxx$1.class;

  • lambda表达式原理:lambda表达式在程序运行的时候,会形成一个类。
    1.在类中新增一个方法,这个方法的方法体就是lambda表达式中的代码
    2.形成一个匿名内部类,实现接口,重写抽象方法

    3.在接口中重写方法会调用新生成的方法

  • jdk自带工具:javap对字节码进行反编译操作

    javap -c -p 文件名.class 
    
    -c:表示对 代码进行反汇编
    -p:显示所有的类和成员
    

1.5lambda表达式的简写写法

在lambda表达式的标准写法基础上,可以使用简写写法规则为:

  1. 小括号内的参数类型可以省掉
  2. 如果小括号内有且仅有一个参数,则小 括号可以省掉
  3. 如果大括号内有且仅有一个语句,可以省掉大括号、return关键字以及语句分号

定义接口:

public interface StudentService {
String show(String name,Integer age);
}


public interface OrderService {
    Integer show(String name);
}

lambda简写写法:

package com.newfeature;

import com.newfeature.service.OrderService;
import com.newfeature.service.StudentService;

public class Demo04lambda {
    public static void main(String[] args) {
     goStudent((String name,Integer age)->{
         return name+age+"***";
     });
     //简写写法
        goStudent((name,age)-> name+age+"0000");
        System.out.println("--------------");
        goOrder((String name)->{
            System.out.println("----->"+name);
            return 999;
        });

        //简写写法
        goOrder(name->{
            System.out.println("----->"+name);
            return 1999;
        });
    }
    public static  void goStudent(StudentService studentService){
        studentService.show("张三",22);
    }
    public static void goOrder(OrderService orderService){
        orderService.show("李四");
    }
}

1.6 lambda表达式的使用前提

  1. 方法的参数 或局部变量类型必须为接口才能使用
  2. 接口中有且仅有一个抽象方法@FunctionalInterface

1.7 lambda表达式与匿名内部类的对比

  • 所需类型不一样

    1. 匿名内部类的类型可以是类,抽象类,接口

    2.lambda表达式需要的类型必须是接口

  • 抽象方法数量不一样
    1匿名内部类所需的接口 中的抽象方法的数量是随意的
    2 lambda表达式所需的接口中只能有一个抽象方法

  • 实现原理不一样
    1 匿名内部类是 在编译后形成一个class
    2 lambda表达式是在程序运行的时候动态生成class

2. 接口中新增的方法

2.1 jdk8之前,接口定义

interface 接口名{
    静态常量;
    抽象方法;
}

jdk8之后,对接口做了增加,接口可以有默认方法和静态方法

interface 接口名{
    静态常量;
    抽象方法;
    默认方法;
    静态方法;
}

2.2 默认方法

package com.newfeature;

public class InterfaceDemo {
    public static void main(String[] args) {
A a=new B();
a.test3();
A c=new C();
c.test3();

    }
}


class  B implements A{

    @Override
    public void test1() {
        System.out.println("test1方法执行了....");
    }

    @Override
    public void test2() {
        System.out.println("test2中方法执行了...");
    }
    
/**
     * 重写默认方法
     * @return
     */
    
    @Override
    public String test3() {
        System.out.println("在B实现类中重写了默认方法test3");
        return "ok...";
    }
}

class C implements A{

    @Override
    public void test1() {

    }

    @Override
    public void test2() {

    }
}
interface A {
    void test1();
    void test2();

    /**
     * 定义接口中默认方法
     * @return
     */
    public default String test3(){
        System.out.println("接口中默认方法执行了....");
        return "hello";
    }
}

接口中默认方法的使用:

  1. 实现类直接调用接口中的默认方法
  2. 实现类重写接口的默认方法

2.3 静态方法

jdk8中为接口新增了静态方法,作用是为了接口的扩展

语法规则

interface 接口名{
    修饰符 static 返回值类型 方法ming{
        方法体;
    }
}
package com.newfeature;

public class InterfaceDemo {
    public static void main(String[] args) {
A a=new B();
a.test3();

A c=new C();
c.test3();

A.test4();

    }
}


class  B implements A{

    @Override
    public void test1() {
        System.out.println("test1方法执行了....");
    }

    @Override
    public void test2() {
        System.out.println("test2中方法执行了...");
    }

    @Override
    public String test3() {
        System.out.println("在B实现类中重写了默认方法test3");
        return "ok...";
    }
}

class C implements A{

    @Override
    public void test1() {

    }

    @Override
    public void test2() {

    }
}
interface A {
    void test1();
    void test2();

    /**
     * 定义接口中默认方法
     * @return
     */
    public default String test3(){
        System.out.println("接口中默认方法执行了....");
        return "hello";
    }

    /**
     * 定义接口中静态方法
     * @return
     */
    public static String test4(){
        System.out.println("接口中的静态方法执行了...");
        return "Hello2";
    }
}

接口中的静态方法使用:

接口中的静态方法在实现类中是不能被重写的,只能通过接口类型来实现:接口名.静态方法名称();

在这里插入图片描述

接口中默认方法与静态方法的区别:

1.默认方法通过实例调用,静态方法通过接口名调用

2.默认方法可以被继承,实现类可以直接调用默认方法,也可以重写接口默认方法

3.静态方法不能被继承,实现类不能重写接口的静态方法,只能用接口名调用

3. 函数式接口

3.1 函数式接口的由来

使用lambda表达式的前提是需要函数式接口,而lambda表达式使用时不关心接口的名称、抽象方法名,只关心抽象方法的参数列表和返回值类型。为了使用lambda表达式更方便,jkd提供了大量的函数式接口

package com.newfeature;

public class FuncInterface {
    public static void main(String[] args) {
        fun1((arr)->{
            int sum=0;
            for(int i:arr){
                sum+=i;
            }
            return sum;
        });
    }
    public static void fun1(Operator operator){
        int[] arr={1,2,3,4};
        int sum=operator.getSum(arr);
        System.out.println("sum="+sum);
    }
}

/**
 * 函数式接口
 */
@FunctionalInterface
interface Operator{
    int getSum(int[] arr);
}

3.2 函数式接口介绍

在jdk中提供的函数式接口,主要在java.util.function包中

1. Supplier: 无参有返回值接口,对于lambda表达式需要提供一个返回数据的类型; 用于生产数据
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

使用:

package com.newfeature;

import java.util.Arrays;
import java.util.function.Supplier;

/**
 * Supplier接口使用
 */
public class SupplierTest {
    private static void fun1(Supplier<Integer> supplier){
        Integer max=supplier.get();
        System.out.println("max="+max);
    }

    public static void main(String[] args) {
        fun1(()->{
            int arr[]={22,33,55,66,34,99};
            //计算数组中最大值
            Arrays.sort(arr);
            return arr[arr.length-1];
        });
    }
}

2. Consumer :有参无返回值的接口,用于消费数据,使用时候需要指定一个泛型来定义参数类型
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

}

使用:将输入的数据统一转换为小写

package com.newfeature;

import java.util.Locale;
import java.util.function.Consumer;

public class ConsumerTest {
    public static void main(String[] args) {
        test(msg->{
            System.out.println(msg+"->转换为小写:"+msg.toLowerCase());
        });
    }
    public static void test(Consumer<String> consumer){
        consumer.accept("Hello World");
    }
}

默认方法andThen使用:如果一个方法的参数和返回值全部是consumer类型,就可以实现效果,消费一个数据的时候,首先做一个操作,然后 再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法->andThen方法。

 default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
具体操作:
package com.newfeature;

import java.util.function.Consumer;

public class ConsumerTest2 {
    public static void main(String[] args) {
        test2(msg1->{
            System.out.println(msg1+"->转换为小写:"+msg1.toLowerCase());
        },msg2->{
            System.out.println(msg2+"转换为大写:"+msg2.toUpperCase());
        });
    }
    public static void test(Consumer<String> consumer){
        consumer.accept("Hello World");
    }
    public  static void  test2(Consumer<String> c1,Consumer<String> c2){
        String str="Hello World";
       // c1.accept(str);//转小写
        //c2.accept(str);//转大写
        //c1.andThen(c2).accept(str);
        c2.andThen(c1).accept(str);
    }
}

3.Funciton:有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
package java.util.function;

import java.util.Objects;


@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);


}

使用:传递进去一个字符串,返回一个integer

package com;

import java.util.function.Function;

public class FunctionTest {
    public static void main(String[] args) {
        test(msg->{
return Integer.parseInt(msg);
        });
    }
    public static void  test(Function<String,Integer> function){
        Integer apply=function.apply("666");
        System.out.println("apply="+apply);
    }

}

默认方法:andThen,也是进行组合操作

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

组合操作使用:

package com;

import java.util.function.Function;

public class FunctionTest {
    public static void main(String[] args) {
        test(msg -> {
            return Integer.parseInt(msg);
        }, msg2 -> {
            return msg2 * 2;
        });
    }

    public static void test(Function<String, Integer> f1, Function<Integer, Integer> f2) {
//        Integer apply=f1.apply("666");
//        Integer apply2=f2.apply(apply);
        Integer apply2 = f1.andThen(f2).apply("666");
        System.out.println("apply2=" + apply2);
    }

}

默认的compose方法作用顺序和andThen方法刚好相反;

静态方法identity则是,输入什么参数就返回什么参数

  default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

static <T> Function<T, T> identity() {
        return t -> t;
    }
4. Predicate:有参且有返回值为boolean型的接口

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);


}

使用:

package com.newfeature;

import java.util.function.Predicate;

public class PredicateTest {
    private  static  void  test(Predicate<String> predicate,String msg){
        boolean b = predicate.test(msg);
        System.out.println("b="+b);
    }

    public static void main(String[] args) {
        test(msg->{
            return msg.length()>3;
        },"a");
    }
}

在Predicate中的默认方法提供了逻辑操作:

  • and
  • or
  • negate:取反
  • isEquals
package com.newfeature;

import java.util.function.Predicate;

public class PredicateTest {
    private  static  void  test(Predicate<String> p1,Predicate<String> p2){
       //p1包含H,p2包含w
        //p1包含H同时p2包含w
       boolean rs1= p1.and(p2).test("Hello");
       //p1包含H或者p2包含w
        boolean rs2=p1.or(p2).test("Hello");
        //p1不包含H
        boolean rs3=p1.negate().test("Hello");
        System.out.println("rs1:"+rs1);//false
        System.out.println("rs2:"+rs2);//true
        System.out.println("rs3:"+rs3);//false
    }

    public static void main(String[] args) {
        test(msg1->{
            return msg1.contains("H");
        },msg2->{
            return msg2.contains("W");
        });
    }
}

4.方法引用

4.1 使用方法引用的原因:

使用lambda表达式的时候,出现代码冗余的情况,eg:用lambda表达式求和

package com.newfeature;

import java.util.function.Consumer;

public class FunctionRefTest {
    private static void printMax(Consumer<int[]> consumer){
    int[] a={10,20,30,50,90};
    consumer.accept(a);
    }

    /**
     * 求数组中值的总和
      * @param a
     */    
public void getTotal(int a[]){
    int sum=0;
    for(int i:a){
        sum+=i;
    }
    System.out.println("数组之和:"+sum);
}
    public static void main(String[] args) {
        //lambda表达式中的代码和getTotal中代码出现冗余了
        printMax(a->{
            int sum=0;
            for(int i:a){
                sum+=i;
            }
            System.out.println("数组之和:"+sum);
        });
    }
}

冗余代码的解决方案:

​ 因为lambda表达式中要执行的代码和另外一个方法中的代码一样,就没必要重写一份代码,就可以使用:引用 重复代码

package com.newfeature;

import java.util.function.Consumer;

public class FunctionRefTest {
    private static void printMax(Consumer<int[]> consumer){
    int[] a={10,20,30,50,90};
    consumer.accept(a);
    }

    /**
     * 求数组中元素的总和
      * @param a
     */
public static void getTotal(int a[]){
    int sum=0;
    for(int i:a){
        sum+=i;
    }
    System.out.println("数组之和:"+sum);
}
    public static void main(String[] args) {
        // :: 方法引用,jdk8的新特性
        printMax(FunctionRefTest::getTotal);
    }
}

:: 方法引用,jdk8的新特性的语法

应用场景:如果Lambda表达式所要实现的方案,已经在其他方法存在相同的方案,那么则可以使用方法引用。

常见的引用方式:

  1. instanceName::methodName 对象::方法名
  2. ClassName::staticMethodName 类名::静态方法
  3. ClassName::methodName 类名::普通方法
  4. ClassName::new 类名::new 调用的构造器
  5. TypeName[]::new String[]::new 调用数组的构造器

4.2 对象名::方法名

package com.newfeature;

import java.util.Date;
import java.util.function.Supplier;

public class FunctionTest03 {
    public static void main(String[] args) {
        Date now = new Date();
        Supplier<Long> supplier = () -> {
            return now.getTime();
        };
        System.out.println(supplier.get());

        //通过引用的方法引用的方式处理
        Supplier<Long> supplier1 = now::getTime;
        System.out.println(supplier1.get());
    }
}

方法引用注意的事项:

  1. 被用户的方法,参数要和接口中的抽象方法的参数一样
  2. 当接口抽象方法有返回值时,被引用的方法必须有返回值

4.3 类名 ::静态方法名

package com.newfeature;

import java.util.function.Supplier;

public class FunctionTest04 {
    public static void main(String[] args) {
        Supplier<Long> supplier1=()->{
            return System.currentTimeMillis();
        };

        System.out.println(supplier1.get());

        //通过 方法引用来实现
        Supplier<Long> supplier2=System::currentTimeMillis;
        System.out.println(supplier2.get());
    }
}

4.4 类名::引用实例方法

java面向对象中,类名只能调用静态方法,类名引用实例方法是使用前提,实际上第一个参数作为方法的调用者

package com.newfeature;


import java.util.function.BiFunction;
import java.util.function.Function;

public class FunctionTest05 {
    public static void main(String[] args) {
        Function<String,Integer> function=(s)->{
            return s.length();
        };
        System.out.println(function.apply("hello"));

        //通过方法引用来实现
        Function<String,Integer> function1=String::length;
        System.out.println(function1.apply("hahah"));

        BiFunction<String,Integer,String> function2=String::substring;
        String msg = function2.apply("helloworld", 3);
        System.out.println(msg);
    }
}

4.5 类名::构造器

package com.newfeature;


import com.newfeature.domain.Person;

import java.util.function.BiFunction;
import java.util.function.Supplier;

public class FunctionTest06 {
    public static void main(String[] args) {
        Supplier<Person> sup=()->{
            return new Person();
        };
        System.out.println(sup.get());
        //通过方法引用来实现
        Supplier<Person> sup1=Person::new;
        System.out.println(sup1.get());

        BiFunction<String,Integer,Person> function=Person::new;
        System.out.println(function.apply("张三",22));
    }
}

4.6 数组::构造器

package com.newfeature;


import java.util.function.Function;

public class FunctionTest07 {
    public static void main(String[] args) {
        Function<Integer, String[]> fun1 = (len) -> {
            return new String[len];
        };

        String[] a1 = fun1.apply(3);
        System.out.println("数值的长度是:" + a1.length);

        Function<Integer, String[]> fun2 = String[]::new;
        String[] a2 = fun2.apply(5);
        System.out.println("数组长度是:" + a2.length);
    }
}

方法引用是lambda表达式符合特定情况下的一种缩写方式,不过方法的引用只能引用已经存在的方法

5. stream API

5.1 集合处理数据的弊端

对集合中元素进行操作的时候,除了必须添加、删除、、获取外,典型的操作就是集合的遍历

package com.newfeature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class StreamTest01 {
    public static void main(String[] args) {
        //定义一个集合
        List<String> list= Arrays.asList("张三","张麻子","李四","王五");
        //1.获取所有,姓张的信息
        List<String> list2=new ArrayList<>();
        for(String s:list){
           if(s.startsWith("张")){
               list2.add(s);
           }
        }

        //2.获取名称长度为3的用户
        List<String> list3=new ArrayList<>();
        for(String s:list2){
            if(s.length()==3){
                list3.add(s);
            }
        }

        //3.输出所有的用户信息
        for(String s:list3){
            System.out.println(s);
        }
    }
}

针对上面的需求,总是一次次的遍历循环,希望有高效的方式处理,通过jdk8中stream api优雅处理

package com.newfeature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class StreamTest02 {
    public static void main(String[] args) {
        //定义一个集合
        List<String> list = Arrays.asList("张三", "张麻子", "李四", "王五");
        list.stream()
                .filter(s -> s.startsWith("张"))
                .filter(s -> s.length() == 3)
                .forEach(System.out::println);
//                .forEach(s -> {System.out.println(s);});


    }
}

上面StreamAPI代码含义:获取流,过滤张,过滤长度,遍历打印;代码更简洁

5.2 stream 流式思想

  • Stream和IO流没有任何关系;
  • Stream流式思想类似工厂车间的生产流水线,stream流不是一种数据结构,不保存数据,而是对数据进行加工处理,stream可以看做是流水线上的一个工序,在流水线上,通过多个工序让一个原材料加工成一个商品
    在这里插入图片描述

在这里插入图片描述

Stream API能让我们快速完成复杂的操作,eg:筛选,切片,映射、查找,去除重复、统计、匹配和归约

5.3 stream流的获取方式

  1. 根据Collection获取
    首先,java.uitl.Collection接口中加入了默认的方法stream,也就是Collection接口下所有的实现都 可以通过stream方法来获取stream流

    在这里插入图片描述

package com.newfeature;


import java.util.*;

public class StreamTest03 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        list.stream();
        Set<String> set=new HashSet<>();
        set.stream();

        Vector<String> vector=new Vector<>();
         vector.stream();

    }
}

但是map接口没有实现Collection接口,怎么做呢?可以根据Map获取对应的key value的集合

package com.newfeature;


import java.util.*;
import java.util.stream.Stream;

public class StreamTest04 {
    public static void main(String[] args) {
        Map<String,Object> map=new HashMap<>();
        Stream<String> stream=map.keySet().stream();//key
        Stream<Object> ojb= map.values().stream();//value
        Stream<Map.Entry<String,Object>> stream2=map.entrySet().stream();//entry


    }
}
  1. 通过stream的of方法

    把数组转换成stream,采用of方法

在这里插入图片描述

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest05 {
    public static void main(String[] args) {
        Stream<String> a1= Stream.of("a1","a2","a3");
        String[]  arr1={"aa","bb","cc"};
        Stream<String> arr11=Stream.of(arr1);
        Integer[] arr2={1,2,3};
        Stream<Integer> arr21=Stream.of(arr2);
        arr21.forEach(System.out::println);
        
        //注意:基本的数据类型是不行的,要用包装类型
        int[] arr3={1,2,3};
        Stream.of(arr3).forEach(System.out::println);



    }
}

5.4 stream常用方法介绍

在这里插入图片描述

  • 终结方法:返回值类型不再是stream类型的方法,不再支持链式调用。eg: foreach() 、count()

  • 非终结方法:返回值类型仍然是stream类型的方法,支持链式调用.(除了终结方法,其余方法均为非终结方法)

注意:

  1. strem只能操作一次

  2. stream返回的是最新的流

  3. stream不调用终结方法,中间的操作的不会执行

    package com.newfeature;
    
    
    import java.util.stream.Stream;
    
    public class StreamTest06 {
        public static void main(String[] args) {
            Stream<String> a1 = Stream.of("a1", "a2", "a3");
            a1.filter(s -> {
                System.out.println("------------");
                return s.contains("a");
            }).forEach(System.out::println);
    
            System.out.println("------------->");
    
    
        }
    }
    

5.5 forEach:遍历数据

void forEach(Consumer<? super T> action);

该方法接受一个Consumer接口,会将每一个流元素交给函数处理

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest06 {
    public static void main(String[] args) {
        Stream.of("a1", "a2", "a3").forEach(System.out::println);
    }
}

5.6 count:统计stream流中元素个数的

long count();
package com.newfeature;


import java.util.stream.Stream;

public class StreamTest06 {
    public static void main(String[] args) {
      long count= Stream.of("a1", "a2", "a3").count();
        System.out.println(count);
    }
}

5.7 filter:用来过滤数据,返回符合条件的数据

在这里插入图片描述

可以通过filter方法将一个流转换为另一个子集流

Stream<T> filter(Predicate<? super T> predicate);

该接口接收一个Predicate函数式接口参数,作为筛选条件
package com.newfeature;


import java.util.stream.Stream;

public class StreamTest07 {
    public static void main(String[] args) {
             Stream.of("a1", "a2", "a3","bb","dd")
              .filter((s)->s.contains("a")).forEach(System.out::println);
    }
}

输出:

a1
a2
a3

5.8 limit:对流进行截取处理,只取前n个数据

在这里插入图片描述

Stream<T> limit(long maxSize);

参数是一个long类型数值,如果集合当前长度大于参数就进行截取,否则不操作

使用:

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest08 {
    public static void main(String[] args) {
             Stream.of("a1", "a2", "a3","bb","dd")
                     .limit(3).forEach(System.out::println);
    }
}

输出:

a1
a2
a3

5.9 skip

在这里插入图片描述

如果想跳过前面几个元素,可以使用skip方法获取一个截取之后的新流:

Stream<T> skip(long n);

操作:

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest09 {
    public static void main(String[] args) {
             Stream.of("a1", "a2", "a3","bb","dd")
                     .skip(3).forEach(System.out::println);
    }
}

输出:

bb
dd

5.10 map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

在这里插入图片描述

该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一个R类型数据

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest10 {
    public static void main(String[] args) {
             Stream.of("1", "2", "3","4","5")
//                     .map(msg->Integer.parseInt(msg))
                     .map(Integer::parseInt)
                     .forEach(System.out::println);
    }
}

5.11 sorted:对数据进行排序

Stream<T> sorted();

Stream<T> sorted(Comparator<? super T> comparator);

在使用时候,可以根据自然规则排序,也可以通过比较器指定对应的排序规则

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest11 {
    public static void main(String[] args) {
             Stream.of("1", "5", "2","3","0")
                     .map(Integer::parseInt)
                     .sorted()  //根据数据的自然顺序排序
                     .sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
                     .forEach(System.out::println);
    }
}

5.12 distinct:去掉重复数据

Stream<T> distinct();

在这里插入图片描述

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest11 {
    public static void main(String[] args) {
             Stream.of("1","3", "5", "2","3","0")
                     .map(Integer::parseInt)
                     .sorted()  //根据数据的自然顺序排序
                     .sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
                     .distinct()  //去除重复记录
                     .forEach(System.out::println);
    }
}

Stream流中的distinct方法对于基本数据类型是可以直接去重的,但对自定义类型,需要重写hashcode & equals方法来移除重复元素

package com.newfeature.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Objects;

//@Data
//@AllArgsConstructor
//@NoArgsConstructor
public class Person {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }


    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) && Objects.equals(age, person.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    // private Integer height;
}


package com.newfeature;


import com.newfeature.domain.Person;

import java.util.stream.Stream;

public class StreamTest11 {
    public static void main(String[] args) {
             Stream.of("1","3", "5", "2","3","0")
                     .map(Integer::parseInt)
                     .sorted()  //根据数据的自然顺序排序
                     .sorted((o1,o2)->o2-o1) //根据比较器指定对应的排序规则
                     .distinct()  //去除重复记录
                     .forEach(System.out::println);
        System.out.println("------------------------");
        Stream.of(new Person("张三",18),
                new Person("李四",33),
                new Person("张三",18)
                ).distinct()
                .forEach(System.out::println);
    }
}

5.13 match

5.14 find

5.15 max&min

Optional<T> max(Comparator<? super T> comparator);

 Optional<T> min(Comparator<? super T> comparator);

max使用:

package com.newfeature;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest12 {
    public static void main(String[] args) {
        Optional<Integer>  max = Stream.of(4, 5, 3, 9)
                        .max((o1,o2)->Integer.compare(o1,o2));
        System.out.println(max.get());

    }
}

min使用:

package com.newfeature;

import java.util.Optional;
import java.util.stream.Stream;

public class StreamTest12 {
    public static void main(String[] args) {
        Optional<Integer>  min = Stream.of(4, 5, 3, 9)
                        .min((o1,o2)->Integer.compare(o1,o2));
        System.out.println(min.get());

    }
}

5.16 reduce方法

在这里插入图片描述

如果需要将所有数据归纳得到一个数据,使用reduce方法

T reduce(T identity, BinaryOperator<T> accumulator);

使用:

package com.newfeature;

import java.util.stream.Stream;

public class StreamTest12 {
    public static void main(String[] args) {
        Integer sum = Stream.of(4, 5, 3, 9)
                .reduce(0, (x, y) -> {
                    //identity默认值
                    //第一次的时候会将默认值赋值给x
                    //之后再次会将上一次的操作结果赋值给x,y是每次从数据中获取的元素
                    System.out.println("x=" + x + ",y=" + y);
                    return x + y;
                });
        System.out.println(sum);
        //获取最大值、
        Integer max = Stream.of(4, 5, 3, 9)
                .reduce(0, (x, y) -> {
                    return x > y ? x : y;
                });
        System.out.println(max);

    }
}
  • map和reduce的组合:实际开发中经常会map与reduce一块使用

  • package com.newfeature;
    
    
    import com.newfeature.domain.Person;
    
    import java.util.stream.Stream;
    
    public class StreamTest13 {
        public static void main(String[] args) {
            //求年龄的总和
            Integer sum = Stream.of(new Person("张三", 18),
                            new Person("李四", 33),
                            new Person("张三", 38),
                            new Person("王五", 45),
                            new Person("张三", 19)
                    ).map(Person::getAge)
                    .reduce(0,Integer::sum);
            System.out.println(sum);
    
            //2.求年龄最大值
            Integer max = Stream.of(new Person("张三", 18),
                            new Person("李四", 33),
                            new Person("张三", 38),
                            new Person("王五", 45),
                            new Person("张三", 19)
                    ).map(Person::getAge)//实现数据类型的转换,符合reduce对数据的要求
                    .reduce(0,Math::max);//reduce实现数据的处理
            System.out.println(max);
    
    //3.统计字符串a出现的次数
           Integer count= Stream.of("a","b","c","d","a")
                    .map(ch->"a".equals(ch)?1:0)
                    .reduce(0,Integer::sum);
            System.out.println(count);
    
        }
    }
    

5.17 mapToInt

作用:将stream中的Integer类型转换为int类型,可使用mapToInt方法
在这里插入图片描述

使用:

package com.newfeature;


import com.newfeature.domain.Person;

import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamTest14 {
    public static void main(String[] args) {
       //Integer占用的内存比int多很多,在stream流操作中会自动装箱和拆箱操作

        Integer arr[]={1,2,3,4,5,7,9};
        Stream.of(arr)
                .filter(i->i>0).forEach(System.out::println);
        System.out.println("--------------------");
        //为提高代码的执行效率,可以将stream中的integer数据先转换为int数据,然后再操作
        IntStream intStream = Stream.of(arr)
                .mapToInt(Integer::intValue);
        intStream.filter(i->i>3).forEach(System.out::println);
    }
}

5.18 concat:如果两个流,合并成为一个流,使用stream的concat方法

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
    Objects.requireNonNull(a);
    Objects.requireNonNull(b);

    @SuppressWarnings("unchecked")
    Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
            (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
    Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
    return stream.onClose(Streams.composedClose(a, b));
}

使用:

package com.newfeature;


import java.util.stream.Stream;

public class StreamTest15 {
    public static void main(String[] args) {
        Stream<String> stream1=Stream.of("a","b","c");
        Stream<String> stream2=Stream.of("g","h","s");
        //通过concat将两个stream合并为一个新的stream
        Stream.concat(stream1,stream2).forEach(System.out::println);
    }
}

5.19 综合使用stream

在这里插入图片描述

package com.newfeature;


import com.newfeature.domain.Person;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamTest16 {
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("a1", "aa2", "a3", "aa4", "a5", "as6", "aa7");
        List<String> list2 = Arrays.asList("b1", "张b2", "b3", "张b4", "张bk5", "b6", "b7");
        Stream<String> stream1 = list1.stream()
                .filter(x -> x.length() == 3)
                .limit(3);
        Stream<String> stream2 = list2.stream().filter(x -> x.startsWith("张")).skip(2);
        Stream.concat(stream1, stream2)
                .map(Person::new)
                .forEach(System.out::println);
    }
}

输出结果:

Person{name='aa2', age=null}
Person{name='aa4', age=null}
Person{name='as6', age=null}
Person{name='张bk5', age=null}

1.结果收集到集合中

package com.newfeature;

import org.junit.Test;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class Test01 {

    /**
     * Stream结果收集到集合中
     */
    @Test
    public void test01() {
        List<String> list = Stream.of("a", "b", "c", "c")
                .collect(Collectors.toList());
        System.out.println(list);

        //收集到set
        Set<String> set = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toSet());
        System.out.println(set);

        //获取的类型为具体的实现,eg:Arraylist,HashSet
        ArrayList<String> arrayList = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toCollection(ArrayList::new));
        //.collect(Collectors.toCollection(()->new ArrayList<>()));
        System.out.println(arrayList);
        HashSet<String> hashSet = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);

    }
}

输出:


[a, b, c, c]
[a, b, c]
[a, b, c, a]
[a, b, c]

Process finished with exit code 0

2.结果收集到数组中

Stream中提供toArray方法将结果放到一个数组中,返回值类型是Object[],如果指定返回类型,则使用重载方法toArray(IntFunction f)方法

/**
 * 数据收集到数组中
 */
@Test
public void test02(){
    Object[] objects = Stream.of("a", "b", "c", "a").toArray();
    System.out.println(Arrays.toString(objects));

    //指定返回的数组中的数据类型
    String[] strings = Stream.of("a", "b", "c", "a").toArray(String[]::new);
    System.out.println(Arrays.toString(strings));
}

5.20 对流中数据做聚合计算

使用stream流处理数据后,可以像数据库的聚合函数一样,对一个字段进行操作,eg:min,max,avg,sum,count.

package com.newfeature;

import com.newfeature.domain.Person;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class Test01 {

    /**
     * Stream结果收集到集合中
     */
    @Test
    public void test01() {
        List<String> list = Stream.of("a", "b", "c", "c")
                .collect(Collectors.toList());
        System.out.println(list);

        //收集到set
        Set<String> set = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toSet());
        System.out.println(set);

        //获取的类型为具体的实现,eg:Arraylist,HashSet
        ArrayList<String> arrayList = Stream.of("a", "b", "c", "a")
                .collect(Collectors.toCollection(ArrayList::new));
        //.collect(Collectors.toCollection(()->new ArrayList<>()));
        System.out.println(arrayList);
        HashSet<String> hashSet = Stream.of("a", "b", "c", "a").collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);

    }

    /**
     * 数据收集到数组中
     */
    @Test
    public void test02() {
        Object[] objects = Stream.of("a", "b", "c", "a").toArray();
        System.out.println(Arrays.toString(objects));

        //指定返回的数组中的数据类型
        String[] strings = Stream.of("a", "b", "c", "a").toArray(String[]::new);
        System.out.println(Arrays.toString(strings));
    }

    /**
     * stream中数据流聚合操作
     */
    @Test
    public void test03() {

        //获取年龄的最大值
        Optional<Person> maxAge = Stream.of(new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
        System.out.println("最大年龄:" + maxAge.get());

        //获取年龄的最小值
        Optional<Person> minAge = Stream.of(new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
        System.out.println("最小年龄:" + minAge.get());

        //求所有人年龄之和
        Integer sumAge = Stream.of(new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                //.collect(Collectors.summingInt(s -> s.getAge()));
                .collect(Collectors.summingInt(Person::getAge));
        System.out.println("所有人年龄之和:" + sumAge);
        //求所有人年龄的平均值
        Double avgAge = Stream.of(
                        new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.averagingInt(Person::getAge));
        System.out.println("所有人年龄的平均值:" + avgAge);

        //统计人员数量
        Long count = Stream.of(
                        new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .filter(p -> p.getAge() > 18)
                .collect(Collectors.counting());
        System.out.println("统计人员数量:" + count);

    }
}

5.21 对流中数据做分组操作

/**
     * stream中数据流分组操作
     */
    @Test
    public void test04() {
        //分组
        Map<String, List<Person>> map = Stream.of(
                        new Person("张三", 18),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.groupingBy(Person::getName));
        map.forEach((k,v)-> System.out.println("k="+k+",v="+v));

        System.out.println("--------------------------------");


//年龄大于等于18成年,否则未成年
        Map<String, List<Person>> m2 = Stream.of(
                        new Person("张三", 17),
                        new Person("李四", 33),
                        new Person("张三", 18))
                .collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));
        m2.forEach((k,v)-> System.out.println("k="+k+",v="+v));


    }

多级分组实现:

/**
 * stream中数据流多级分组操作
 */
@Test
public void test05() {
    //根据name,age(成年与未成年)分组
    Map<String, Map<Object,List<Person>>> map=Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .collect(Collectors.groupingBy(Person::getName,Collectors.groupingBy(p->p.getAge()>=18?"成年":"未成年")
    ));
    map.forEach((k,v)->{
        System.out.println("k="+k+",v="+v);
        v.forEach((k1,v1)->{
            System.out.println("\t"+k1+"="+v1);
        });
    } );


}

输出结果:

k=李四,v={成年=[Person{name='李四', age=33}]}
	成年=[Person{name='李四', age=33}]
k=张三,v={未成年=[Person{name='张三', age=17}], 成年=[Person{name='张三', age=18}]}
	未成年=[Person{name='张三', age=17}]
	成年=[Person{name='张三', age=18}]

5.22 对流中数据做分区操作

Collectors.partitioningBy根据值是否为true,把集合中的数据分割 为两个列表,一个true列表,一个false列表

在这里插入图片描述

@Test
public void test06() {
    Map<Boolean, List<Person>> m = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .collect(Collectors.partitioningBy(p -> p.getAge() > 18));
    m.forEach((k, v) -> {
        System.out.println(k + "\t" + v);
    });
}

输出结果:

false	[Person{name='张三', age=17}, Person{name='张三', age=18}]
true	[Person{name='李四', age=33}]

5.23 对流中数据做拼接

collectors.joining根据指定的连接符,将 所有的元素拼接成一个字符串

@Test
public void test07() {
    String s1 = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .map(Person::getName).collect(Collectors.joining());
    System.out.println(s1);

    String s2 = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .map(Person::getName).collect(Collectors.joining("-"));
    System.out.println(s2);

    String s3 = Stream.of(
                    new Person("张三", 17),
                    new Person("李四", 33),
                    new Person("张三", 18))
            .map(Person::getName).collect(Collectors.joining("-","####","$$$$$"));
    System.out.println(s3);
}

5.24 并行的stream流

  • 串行的stream流:在一个线程上执行

    /**
     * 串行流
     */
    @Test
    public void test01() {
        long count = Stream.of(5, 6, 9, 0, 3)
                .filter(s -> {
                    System.out.println(Thread.currentThread() + " " + s);
                    return s > 3;
                }).count();
    }
    

输出结果:

Thread[main,5,main] 5
Thread[main,5,main] 6
Thread[main,5,main] 9
Thread[main,5,main] 0
Thread[main,5,main] 3
  • 并行流
    parallelStream是一个并行执行的流,通过默认的ForkJoinPool,可以提高多线程任务的速度。

    获取并行流的2种方式:

    1.通过list接口中的parallelStream()方法获取

    2.通过已有的串行流转换为并行流 parallel()

    /**
     * 获取并行流的两种方式
     */
    @Test
    public void test02() {
       List<Integer> list=new ArrayList<>();
    //通过list接口直接获取并行流
        Stream<Integer> integerStream=list.parallelStream();
        
        //将已有的串行流转换为并行流
        Stream<String> parallel = Stream.of("1,2,3").parallel();
    
    
    }
    
/**
 * 并行流的操作
 */
@Test
public void test03() {
    long count = Stream.of(1, 2, 3)
            .parallel()//将流转换为并发流,stream处理的时候会通过多线程处理
            .filter(s -> {
                System.out.println(Thread.currentThread() + " s=" + s);
                return s > 2;
            }).count();
    System.out.println(count);

}

输出:

Thread[ForkJoinPool.commonPool-worker-2,5,main] s=3
Thread[main,5,main] s=2
Thread[ForkJoinPool.commonPool-worker-1,5,main] s=1
1

5.25 并行流与串行流对比

通过for循环,串行stream流,并行流stream对八亿个数字求和,对比消耗时间

package com.newfeature;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.stream.LongStream;

public class Test03 {
    private  static long times=800000000;
    private long start;

    @Before
    public void before(){
start=System.currentTimeMillis();
    }

    @After
    public void end(){
        long end=System.currentTimeMillis();
        System.out.println("消耗时间:"+(end-start));

    }

    /**
     * 普通for循环
     */
    @Test
    public void test01(){
        System.out.println("普通for循环:");
        long res=0;
        for(int i=0;i<times;i++){
            res+=i;
        }
    }

    /**
     * 串行流处理
     */
    @Test
    public void test02(){
        System.out.println("串行流:serialStream");
        LongStream.rangeClosed(0,times)
                .reduce(0,Long::sum);
    }

    /**
     * 并行流处理
     */
    @Test
    public void test03(){
LongStream.rangeClosed(0,times)
        .parallel()
        .reduce(0,Long::sum);
    }

}

通过案例,可以看到parallelStream的效率最高

Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,表示每个任务都是一个线程操作

5.26 并行流的多线程安全问题

  • 多 线程处理数据的,线程安全问题

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test04 {
        //并行流多线程安全问题
        @Test
        public void test01(){
     List<Integer> list=new ArrayList<>();
     for(int i=0;i<1000;i++){
         list.add(i);
     }
            System.out.println(list.size());
    
     //使用并行流来向集合中添加数据
            List<Integer> listNew=new ArrayList<>();
            list.parallelStream()
                    .forEach(listNew::add);
            System.out.println(listNew.size());
        }
    }
    

    输出结果:

    1000
    950
    

    或者报异常,针对的解决方案:

    • 加同步锁
    • 使用线程安全的容器
    • 通过stream中的toArray或collect操作
     /**
         * 加同步锁
         */
        @Test
        public void test02() {
            List<Integer> list = new ArrayList<>();
    
            for (int i = 0; i < 1000; i++) {
                list.add(i);
            }
            System.out.println(list.size());
    
            Object obj = new Object();
            //使用并行流来向集合中添加数据
            List<Integer> listNew = new ArrayList<>();
            list.parallelStream()
    //                .forEach(listNew::add);
                    .forEach(s -> {
                        synchronized (obj) {
                            listNew.add(s);
                        }
                    });
            System.out.println(listNew.size());
        }
    /**
     * 同步枷锁
     */
    @Test
    public void test03() {
        List<Integer> listNew = new ArrayList<>();
        Object obj = new Object();
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    synchronized (obj) {
                        listNew.add(i);
                    }
                });
        System.out.println(listNew.size());
    }
    
     /**
         * 使用线程安全的容器
         */
        @Test
        public void test04() {
            Vector v = new Vector();
            IntStream.rangeClosed(1, 1000)
                    .parallel()
                    .forEach(i -> {
    //                    synchronized (obj) {
                            v.add(i);
    //                    }
                    });
            System.out.println(v.size());
        }
    
    
        /**
         * 将线程不安全的容器转换为线程安全的容器
         */
        @Test
        public void test05() {
            List<Integer> listNew=new ArrayList<>();
            //将线程不安全的容器,包装成线程安全的容器
            List<Integer> synchronizedList= Collections.synchronizedList(listNew);
            IntStream.rangeClosed(1, 1000)
                    .parallel()
                    .forEach(i -> {
                        synchronizedList.add(i);
    //                    }
                    });
            System.out.println(synchronizedList.size());
        }
    
    
        /**
         * 通过Stream中的toArray方法或collect方法来操作就是满足线程安全的
         */
        @Test
        public void test06() {
            List<Integer> list = IntStream.rangeClosed(1, 1000)
                    .parallel()
                    .boxed()
                    .collect(Collectors.toList());
    
            System.out.println(list.size());
        }
    

5.27 Fork/Join框架

parallelStream使用的是Fork/Join框架,fork/join框架自jdk7引入,Fork/join框架可以将一个大任务拆分为很多小任务异步执行,fork/join框架主要包括三个模块:

  1. 线程池:ForkJoinPool

  2. 任务对象:ForkJoinTask

  3. 执行任务的线程: ForkJoinWorkerThread

    在这里插入图片描述

  • fork/join原理:采用分治法(Divide-and-Conquer Algorithm)来解决问题。eg:快速排序算法
    在这里插入图片描述

  • fork/join原理-工作窃取算法(work-stealing):
    利用现代硬件设备多核,在一个操作时候,会有空闲的cpu,利用空闲的cpu提高性能。工作窃取(work-stealing)算法是整个fork/join框架的核心理念,是指一个线程从其他队列里窃取任务来执行。

    在这里插入图片描述

  • fork/join使用

    需求:使用fork/join计算1~10000的和,当任务计算的数量大于3000,拆分任务,数量小于3000不拆分任务计算。

在这里插入图片描述

实现:

package com.newfeature;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class Test05 {
    public static void main(String[] args) {
       long start=System.currentTimeMillis();

        ForkJoinPool pool=new ForkJoinPool();
        SumRecursiveTask task=new SumRecursiveTask(1,10000L);
        Long result = pool.invoke(task);
        System.out.println("result="+result);
        long end=System.currentTimeMillis();
        System.out.println("总耗时:"+(end-start));
    }
}


class SumRecursiveTask extends RecursiveTask<Long> {
    //定义拆分的临界值
    private static  final long  THRESHOLD=3000L;

    private  final long start;
    private final  long end;

    public SumRecursiveTask(long start, long end) {
        this.start = start;
        this.end = end;
    }


    @Override
    protected Long compute() {
        long length=end-start;
        if(length<=THRESHOLD){
            //任务不用拆分,可以计算
            long sum=0;
            for(long i=start;i<=end;i++){
                sum+=i;
            }

            System.out.println("计算:"+start+"-->"+end+",的结果:"+sum);
            return sum;
        }else{
           //数量大于预定的数量,说明任务还需要拆分
           long middle=(start+end)/2;
            System.out.println("拆分:左边->"+start+"-->"+middle+",右边-->"+(middle+1)+"-->"+end);
            SumRecursiveTask left=new SumRecursiveTask(start,middle);
            left.fork();
            SumRecursiveTask right=new SumRecursiveTask(middle+1,end);
            right.fork();
            return left.join()+right.join();
        }
    }
}

输出结果:

拆分:左边->1-->5000,右边-->5001-->10000
拆分:左边->1-->2500,右边-->2501-->5000
拆分:左边->5001-->7500,右边-->7501-->10000
计算:7501-->10000,的结果:21876250
计算:2501-->5000,的结果:9376250
计算:5001-->7500,的结果:15626250
计算:1-->2500,的结果:3126250
result=50005000
总耗时:18

6.optional类

Optional类主要用来解决空指针的问题

6.1 以前对null的处理

  @Test
    public void Test01(){
//        String username="张三";
        String username=null;
        if(username!=null){
            System.out.println("字符串的长度:"+username.length());
        }else{
            System.out.println("字符串为空");
        }

    }

6.2 jdk1.8对null的处理->optional类

optional类是一个没有子类的工具类,optional是一个可以为null的容器对象,主要作用:就是为了避免Null检查,防止NullPoiniterException

在这里插入图片描述

6.3 optional基本使用

optional对象的创建方式:

package com.newfeature;

import org.junit.Test;

import java.util.Optional;

public class Test06 {
    @Test
    public void Test01(){
//        String username="张三";
        String username=null;
        if(username!=null){
            System.out.println("字符串的长度:"+username.length());
        }else{
            System.out.println("字符串为空");
        }

    }

    /**
     * optional对象的创建方式
     */
    @Test
    public void test02(){
        //通过of方法,of方法不支持null的
        Optional<String> op1 = Optional.of("张三");
        //Optional<Object> op2 = Optional.of(null);

        //第二种通过ofNullable方法支持null
        Optional<String> op3 = Optional.ofNullable("lisi");
        Optional<Object> op4 = Optional.ofNullable(null);

        //第三种方式通过empty方法创建一个空的optional对象
        Optional<Object> op5 = Optional.empty();
    }

}

常用方法介绍:

/**
 * optional中常用方法介绍
 * get():如果optional有值就返回,否则抛出NoSuchElementException异常,
 *       get()通常和isPresent方法一块使用
 *  isPresent():判断元素是否存在,有返回true,无返回false
 *  orElse(T t):如果调用对象包含值,返回值,否则 返回t
 *  orElseGet(Supplier s):如果调用对象包含值,就返回值,否则返回Lambda表达式的返回值
 */
@Test
public void test03(){
    Optional<String> op1=Optional.of("张三");
    Optional<String> op2=Optional.empty();

  //获取optional中的值
    if(op1.isPresent()){
        String s1=op1.get();
        System.out.println("用于名:"+s1);
    }
   if(op2.isPresent()){
       System.out.println(op2.get());
   }else {
       System.out.println("op2是一个空Optional对象");
   }


   String s3=op1.orElse("李四");
    System.out.println(s3);
    System.out.println(op2.orElse("jackma"));

    String s5 = op2.orElseGet(()->{
        return "hello666";
    });
    System.out.println(s5);
}



  @Test
    public void test04() {
        Optional<String> op1 = Optional.of("张三");
        Optional<String> op2 = Optional.empty();
        //op1.ifPresent(s -> System.out.println("有值:"+s));
        op1.ifPresent(System.out::println);

        op2.ifPresentOrElse(System.out::println, () -> {
            System.out.println("op2 对象为null");
        });
    }

    /**
     * 自定义一个方法,将person对象中name转换为大写并返回
     */
    @Test
    public void test05() {
        Person p = new Person("zhangsan", 10);
        Optional<Person> op = Optional.of(p);
        String name = getNameForOptional(op);
        System.out.println("name:" + name);

    }

    public String getNameForOptional(Optional<Person> op) {
        if (op.isPresent()) {
            String name = op
                    //.map(p -> p.getName())
                    .map(Person::getName)
                    //.map(p -> p.toUpperCase())
                    .map(String::toUpperCase)
                    .orElse("空值");
            return name;

        }
        return null;
    }

    /**
     * 根据person对象,将name转换为大写并返回
     *
     * @param person
     * @return
     */
    public String getName(Person person) {
        if (person != null) {
            String name = person.getName();
            if (name != null) {
                return name.toUpperCase();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

7 .jdk1.8 日期&时间新API

  • 旧版的日期处理:1.设计不合理;2.存在线程不安全的问题->java.uitl.Date,所有的日期类都是可变的;3.时区处理麻烦,日期类不提供国际化,没有时区支持。

  • 新的日期API介绍:
    在这里插入图片描述

  • jdk新的日期api常用操作:

    package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    
    public class Test07 {
    
        /**
         * jdk1.8日期操作 LocalDate
         */
        @Test
        public  void test01(){
            //1.创建指定的日期
            LocalDate date1 = LocalDate.of(2021, 05, 02);
            System.out.println("date1="+date1);
    
            //2.得到当前的日期
            LocalDate now = LocalDate.now();
            System.out.println("now="+now);
            //3.根据localdate对象获取对应的日期信息
            System.out.println("Year:"+now.getYear());
            System.out.println("Month:"+now.getMonth());
            System.out.println("day:"+now.getDayOfMonth());
            System.out.println("week:"+now.getDayOfWeek().getValue());
        }
    
        /**
         * LocalTime 时间操作
         */
        @Test
        public  void test02() {
            //1.获取指定的时间
            LocalTime time=LocalTime.of(5,26,33,23145);
            System.out.println(time);
            //2.获取当前的时间
            LocalTime now=LocalTime.now();
            System.out.println(now);
    
            //3.获取时间信息
            System.out.println(now.getHour());
            System.out.println(now.getMinute());
            System.out.println(now.getSecond());
            System.out.println(now.getNano());
        }
    
        /**
         * 时期时间类型 LocalDateTime
         */
        @Test
        public void test03(){
            //1.获取指定的日期时间
            LocalDateTime dateTime = LocalDateTime.of(2020, 06, 01, 12, 12, 33, 213);
            System.out.println(dateTime);
            //2.获取指定的日期时间
            LocalDateTime now = LocalDateTime.now();
            System.out.println(now);
    
            //获取日期时间信息
            System.out.println(now.getYear());
            System.out.println(now.getMonth());
            System.out.println(now.getDayOfMonth());
            System.out.println(now.getDayOfWeek());
            System.out.println(now.getHour());
        }
    }
    
  • 日期时间的修改和比较

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    
    public class Test08 {
        /**
         * 日期时间的修改
         */
        @Test
        public  void test01(){
            LocalDateTime now=LocalDateTime.now();
            System.out.println("now:"+now);
            //修改日期时间,对日期时间的修改,对已存在的LocalDate对象,创建了模版,并不会修改原来的信息
            LocalDateTime localDateTime=now.withYear(1988);
            System.out.println("now:"+now);
            System.out.println("修改后的:"+localDateTime);
            System.out.println("月份:"+now.withMonth(04));
            System.out.println("天:"+now.withDayOfMonth(25));
            System.out.println("小时:"+now.withHour(8));
    
            //对当前日期时间的基础上,加上或减去指定的时间
            System.out.println("两天后:"+now.plusDays(2));
            System.out.println("10年后:"+now.plusYears(10));
            System.out.println("6个月后: " + now.plusMonths(6));
    
            System.out.println("10年前: " + now.minusYears(10));
            System.out.println("半年前:" + now.minusMonths(6));
            System.out.println("一周前:" + now.minusDays(7));
        }
    
        /**
         * 日期时间比较
         */
        @Test
        public void test02(){
            LocalDate now=LocalDate.now();
            LocalDate date=LocalDate.of(2020,1,22);
    
            //jdk8实现日期比较
            System.out.println("now.isAfter(date) = " + now.isAfter(date));
            System.out.println("now.isBefore(date) = " + now.isBefore(date));
            System.out.println("now.isEqual(date) = " + now.isEqual(date));
    
        }
    }
    

注意:在进行日期时间修改时候,原来的localdate对象是不会被修改的,每次操作是返回一个新的localdate对象,所以多线程场景下是数据安全的

  • 日期时间格式化
    在jdk8中通过java.time.format.DateTimeFormatter类进行日期的解析和格式化操作

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    public class Test8 {
        /**
         * 日期时间格式化
         */
        @Test
        public void test01(){
    
            LocalDateTime now=LocalDateTime.now();
            //指定格式,使用系统默认格式
            DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
            //将日期时间转换为字符串
            String format = now.format(isoLocalDateTime);
            System.out.println("format = " + format);
    
            //通过ofPattern方法来指定特定的格式
            DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String format1 = now.format(dateTimeFormatter1);
            System.out.println("format1 = " + format1);
    
            //将字符串解析为一个日期时间类型
            LocalDateTime parse = LocalDateTime.parse("1997-12-13 22:34:12", dateTimeFormatter1);
            System.out.println("parse = " + parse);
    
    
        }
    }
    
  • Instant类: jdk8中新增一个时间戳,内部保存了从1970年1月1日00:00:00以来的秒和纳秒

  • /**
     * Instant时间戳:可以用来统计时间消耗
     */
    @Test
    public void test02() throws InterruptedException {
        Instant now = Instant.now();
        System.out.println("now = " + now);
        //获取从1970年1月1日 00:00:00到现在的纳秒
        System.out.println(now.getNano());
    
        Thread.sleep(5);
       Instant now1= Instant.now();
        System.out.println("耗时:"+(now1.getNano()-now.getNano()));
    }
    
  • 计算日期时间差
    Jdk8提供了工具类:Duration/Period:计算日期时间差
    1.Duration:用来计算两个时间差(LocalTime)
    2.Period:用来计算两个日期差(LocalDate)

    package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.Duration;
    import java.time.LocalDate;
    import java.time.LocalTime;
    import java.time.Period;
    
    public class Test9 {
        /**
         * 计算日期时间差
         */
        @Test
        public  void test01(){
            //计算时间差
            LocalTime now = LocalTime.now();
            LocalTime time = LocalTime.of(22, 45, 59);
            System.out.println("now = " + now);
    
            //通过Duration计算 时间差
            Duration duration = Duration.between(now,time);
            System.out.println(duration.toDays());
            System.out.println(duration.toHours());
            System.out.println(duration.toMinutes());
            System.out.println(duration.toSeconds());
            System.out.println(duration.toMillis());
    
            //计算日期差
            LocalDate nowDate = LocalDate.now();
            LocalDate date = LocalDate.of(1997, 12, 5);
            Period period = Period.between(date, nowDate);
            System.out.println("period.getYears() = " + period.getYears());
            System.out.println("period.getMonths() = " + period.getMonths());
            System.out.println("period.getDays() = " + period.getDays());
    
        }
    }
    
  • 时间校正器:将日期调整到下个月第一天 等操作,通过时间矫正器操作
    1.TemporalAdjuster:时间 校正器
    2.TemporalAdjusters:通过该类静态方法提供的常用TemporalAdjuster的实现

  • /**
     * 时间校正器
     */
    @Test
    public  void test2(){
        LocalDateTime now = LocalDateTime.now();
        //将当前的日期调整到下个月的1号
        TemporalAdjuster adjuster=temporal -> {
            LocalDateTime dateTime= (LocalDateTime) temporal;
            LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);
            System.out.println("nextMonth = " + nextMonth);
            return  nextMonth;
        };
    
        //通过TemporalAdjusters来实现
        //LocalDateTime nextMonth = now.with(adjuster);
        LocalDateTime nextMonth=now.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("nextMonth = " + nextMonth);
    
    }
    
  • 日期时间的时区
    jdk8中加入对时区的支持,LocalDate,LocalTime,LocalDateTime是不带时区的,带时区的日期时间类分别是:ZonedDate,ZonedTime,ZoneDateTime;其中每个时区都对应着ID,ID的格式为:区域/城市 eg:Asia/Shanghai等;zoneId:该类中包含的所有的时区信息

  • package com.newfeature;
    
    import org.junit.Test;
    
    import java.time.Clock;
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    
    public class Test10 {
        /**
         * 时区操作
         */
        @Test
        public  void test01(){
            //1.获取所有的时区id
            //ZoneId.getAvailableZoneIds().forEach(System.out::println);
    
    
            //获取当前时间,使用的是东八区时区;比标准的时间早8个小时
            LocalDateTime now=LocalDateTime.now();
            System.out.println("now = " + now);
            //获取标准时间
            ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC());
            System.out.println("bz = " + bz);
    
            //使用计算机默认的时区,创建日期时间
            ZonedDateTime now1=ZonedDateTime.now();
            System.out.println("now1 = " + now1);
    
            //使用指定的时区创建日期时间
            ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Marigot"));
            System.out.println("now2 = " + now2);
        }
    }
    

jdk新的日期和时间API的优点:

1.新的日期时间API中,日期和时间对象是不可变,操作日期不会影响原来的值,而是生成一个新的实例

2.提供了两种不同的方式,区分人和机器的操作
3.TemporalAdjuster可以更精确的操作日期,还可以自定义日期调整期
4.线程安全

8 其他新特性

1.重复注解 :@Repeatable
使用步骤:
step1 定义重复注解容器:

package com.newfeature;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[]  value();

}

step2 定义一个可以重复的注解:

package com.newfeature;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

step3 配置多个注解:

package com.newfeature;

@MyAnnotation("test1")
@MyAnnotation("test12")
@MyAnnotation("test13")
public class AnnoTest01 {

    @MyAnnotation("fun1")
    @MyAnnotation("fun2")
    public void test01(){

}

  
}

step4解析得到指定的注解:

/**
 * 解析重复注解
 * @param args
 */
public static void main(String[] args) throws NoSuchMethodException {
   //获取类标注的重复注解
    MyAnnotation[] annotationsByType = AnnoTest01.class.getAnnotationsByType(MyAnnotation.class);
    for (MyAnnotation myAnnotation : annotationsByType) {
        String value = myAnnotation.value();
        System.out.println("value = " + value);
    }

    //获取方法上标注的重复注解
    MyAnnotation[] test01s = AnnoTest01.class.getMethod("test01").getAnnotationsByType(MyAnnotation.class);
    for (MyAnnotation test01 : test01s) {
        System.out.println("test01 = " + test01.value());
    }
}

2.类型注解
jdk8为@Target元注解新增两个类型:
TYPE_PARAMETER 表示该注解能写在类型参数的声明语句中。类型参数 声明如:
step1定义TYPE_PARAMETER :

package com.newfeature;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParam {
}

step2 使用 :

package com.newfeature;

public class TypeDemo01<@TypeParam T> {
 public <@TypeParam  K extends  Object> K test01(){
     return null;
 }
}

TYPE_USE 表示注解可以在任何用到的类型的地方使用.
step1定义

package com.newfeature;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE_USE)
public @interface NotNull {
}

step2使用

package com.newfeature;

public class TypeUseDemo01 {
    public @NotNull  Integer age=10;

    public  Integer sum(@NotNull Integer a,@NotNull Integer b){
        return a+b;
    }
}
  • 33
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值