Java8新特性

934 阅读7分钟

Lambda表达式

Lambda是一个匿名函数,我们可以把Lambda表达式理解成是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁,更灵活的代码,作为一种更紧凑的代码发风格,使得Java的语言表达能力得到了提升。

Lambda表达式在Java8语言中引入的一种新的语法元素和操作符,这个操作符为"->",该操作符被称为Lambda操作符或者箭头操作符,它将Lambda分为两个部分:

  • 左侧:指定了Lambda表达式需要的参数列表
  • 右侧:指定了Lambda体,是抽象方法的实现逻辑,也即Lambda表达式要执行的功能。
import org.junit.Test;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Consumer;

/**
 * Lambda表达式的使用
 * <p>
 * 1.举例: (o1,o2) -> Integer.compare(o1,o2);
 * 2.格式:
 * -> :lambda操作符 或 箭头操作符
 * ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
 * ->右边:lambda体 (其实就是重写的抽象方法的方法体)
 * <p>
 * 3. Lambda表达式的使用:(分为6种情况介绍)
 * <p>
 * 总结:
 * ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也
 * 可以省略
 * ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一
 对{}和return关键字
 * <p>
 * 4.Lambda表达式的本质:作为函数式接口的实例
 * <p>
 * 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上
 使用 @FunctionalInterface 注解,
 * 这样做可以检查它是否是一个函数式接口。
 * <p>
 * 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
 */
public class LambdaTest1 {
    //语法格式一:无参,无返回值
    @Test
    public void test1() {
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("我爱北京天安门");
            }
        };

        r1.run();

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

        Runnable r2 = () -> {
            System.out.println("我爱北京故宫");
        };

        r2.run();
    }

    //语法格式二:Lambda 需要一个参数,但是没有返回值。
    @Test
    public void test2() {

        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("谎言和誓言的区别是什么?");

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

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");

    }

    //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
    @Test
    public void test3() {

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");

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

        Consumer<String> con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("一个是听得人当真了,一个是说的人当真了");

    }

    @Test
    public void test4() {

        ArrayList<String> list = new ArrayList<>();//类型推断

        int[] arr = {1, 2, 3};//类型推断

    }

    //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
    @Test
    public void test5() {
        Consumer<String> con1 = (s) -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");

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

        Consumer<String> con2 = s -> {
            System.out.println(s);
        };
        con2.accept("一个是听得人当真了,一个是说的人当真了");


    }

    //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
    @Test
    public void test6() {

        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            }
        };

        System.out.println(com1.compare(12, 21));

        System.out.println("*****************************");
        Comparator<Integer> com2 = (o1, o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };

        System.out.println(com2.compare(12, 6));


    }

    //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
    @Test
    public void test7() {

        Comparator<Integer> com1 = (o1, o2) -> {
            return o1.compareTo(o2);
        };

        System.out.println(com1.compare(12, 6));

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

        Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2);

        System.out.println(com2.compare(12, 21));

    }

    @Test
    public void test8() {
        Consumer<String> con1 = s -> {
            System.out.println(s);
        };
        con1.accept("一个是听得人当真了,一个是说的人当真了");

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

        Consumer<String> con2 = s -> System.out.println(s);

        con2.accept("一个是听得人当真了,一个是说的人当真了");

    }

}

内置四大函数接口

  • 消费型接口: Consumer< T> void accept(T t)有参数,无返回值的抽象方法;
/**
 * 消费型接口
 */
public class ConsumerInterface {
    public static void main(String[] args) {
        Consumer<String> stringConsumer = new Consumer<String>() {
            @Override
            public void accept(String str) {
                System.out.println(str);
            }
        };
 
        //Lambda表达式简化
        Consumer<String> consumer =(str)->{System.out.println(str);};
        consumer.accept("打印str");
    }
}

  • 供给型接口: Supplier < T> T get() 无参有返回值的抽象方法;
/**
 * 供给型接口
 */
public class SupplierInterface {
    public static void main(String[] args) {
        Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get() {
                return "供给型接口";
            }
        };
 
        //Lambda表达式简化
        Supplier<String> sp =()->{return "供给型接口";};
 
        System.out.println(sp.get());
    }

  • 断定型接口: Predicate boolean test(T t):有参,但是返回值类型是固定的boolean;
/**
 * Predicate
 *有一个输入参数,返回值只能是boolean值,用于做判断
 */
 
public static void main(String[] args) {
    //可以来做判断字符串是否为空
    Predicate pc = new Predicate<String>() {
        @Override
        public boolean test(String str) {
 
            return str.isEmpty();
        }
    };
 
    System.out.println(pc.test("qwew"));
 
    //Lambda表达式简化
    Predicate<String> pre =(str)->{return str.isEmpty(); };
    System.out.println(pre.test(""));
}

  • 函数型接口: Function<T,R> R apply(T t)有参有返回值的抽象方法;
/**
 * Function
 *有参数,有返回值
 */
public static void main(String[] args) {
    Function function = new Function<String, String>() {
        @Override
        public String apply(String str) {
            return str;
        }
    };
    
    //Lambda表达式简化
    Function<String, String> function2 = (str) -> {return str; };
 
    System.out.println(function2.apply("asd"));
 
}

Stream流

我们可以使用 java.util.Stream 对一个包含一个或多个元素的集合做各种操作。这些操作可能是 中间操作 亦或是 终端操作。 终端操作会返回一个结果,而中间操作会返回一个 Stream 流。

Stream流的方法详解

forEach

forEach()方法遍历流中的元素

void forEach(Consumer<? super T> action)

 List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.stream().forEach((str)->{
            System.out.println(str);
        });
 //简写
 list.stream().forEach(System.out::println);

Filter

筛选过滤出符合指定条件的元素

public static void main(args[]){
    List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
 
    System.out.println("Languages which starts with J :");
    filter(languages, (str)->str.startsWith("J"));
 
    System.out.println("Languages which ends with a ");
    filter(languages, (str)->str.endsWith("a"));
 
    System.out.println("Print all languages :");
    filter(languages, (str)->true);
 
    System.out.println("Print no language : ");
    filter(languages, (str)->false);
 
    System.out.println("Print language whose length greater than 4:");
    filter(languages, (str)->str.length() > 4);
}
 
public static void filter(List names, Predicate condition) {
    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
        System.out.println(name + " ");
    });
}

Predicate

// 可以用and()、or()和xor()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));

limit

从流中筛选出前n个元素

Stream<T> limit(long maxSize);

//筛选出流中前两个元素
list.stream().limit(2).forEach(System.out::println);

map

map()方法,将流中的元素映射到另外一个流中

//该方法需要一个Function型函数式接口,将当前流中T类型的数据转换成R类型的流
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
 public static void main(String[] args) {
        Stream.of("1", "2", "3", "4", "5")
                .map(Integer::parseInt)
                .forEach(System.out::println);
        Stream.of(6,7,8,9,10)
                .map(String::valueOf)
                .forEach(System.out::println);
    }

skip

跳过前面n个元素,进行截取

Stream<T> skip(long n);

使用:

public static void main(String[] args) {
        Stream.of("a", "b", "c", "d", "a", "b", "a", "c")
                .skip(5)
                //.skip(-1) 报错
                //.skip(10)
                .forEach(System.out::println);
    }

concat

concat()方法,Stream接口中的静态方法,合并两个流

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

使用:

List<Integer> list = new ArrayList<>();
        list.add(12);
        list.add(8);
        list.add(4);
        list.add(13);

        Stream<Integer> stream1 = list.stream().filter((str) -> {
            return str > 12;
        });

        Stream<Integer> stream2 = list.stream().filter((str) -> {
            return str < 5;
        });

       Stream.concat(stream1, stream2).forEach(System.out::println); //13 4

reduce

将所有数据归纳得到一个数据

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

使用:

public static void main(String[] args) {
        Integer sum = Stream.of("1", "2", "3", "4", "5","1","1","2")
                .map(Integer::parseInt)
                // identity:默认值
                // 第一次的时候会将默认值赋值给a
                // 之后每次都会将上次的结果赋值给a,b接收流中的数据
                .reduce(0, (a, b) -> {
                    System.out.print("a = " + a);
                    System.out.println(" b = " + b);
                    return a + b;
                });
        System.out.println("sum = " + sum);
        // 获取最大值:
        Integer max = Stream.of("1", "2", "3", "4", "5")
                .map(Integer::parseInt)
                .reduce(0, (a, b) -> a > b ? a : b);
                //.reduce(1, Math::max);
        System.out.println("max = "+max);
        // 统计 1 出现的次数
        Integer count = Stream.of("1", "2", "3", "4", "5", "1", "1", "2")
                .map(s -> "1".equals(s) ? 1 : 0)
                .reduce(0, Integer::sum);
        System.out.println(count);
    }

max,min

max:获取最大值

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

min:获取最小值

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

使用:

    public static void main(String[] args) {
        Optional<Integer> max = Stream.of("1", "2", "3", "4", "5")
                .map(Integer::parseInt)
                .max(Comparator.comparingInt(o -> o));
                // .max((o1,o2)->o1-o2);等价
        System.out.println("max = "+max.get());
        Optional<Integer> min = Stream.of("1", "2", "3", "4", "5")
                .map(Integer::parseInt)
                .min((o1,o2)->o1-o2);
        System.out.println("min = "+min.get());
    }

distinct

除去重复的元素

Stream<T> distinct();

使用:

 public static void main(String[] args) {
        Stream<String> distinct = Stream.of("a", "b", "c", "d", "a", "b", "a", "c")
                .distinct();
        distinct.forEach(System.out::println);

        // 需要在Student类中重写equals和hashcode方法,才能去掉重复的数据
        Stream.of(new Student("张三",18),
                new Student("李四",20),
                new Student("张三",18),
                new Student("王五",22),
                new Student("李四",20))
                .distinct()
                .forEach(System.out::println);
    }

allMatch、anyMatch、nonMatch

allMatch()、anyMatch()、nonMatch()方法,判断数据是否匹配指定条件

//allMatch():元素是否全部都匹配条件
boolean allMatch(Predicate<? super T> predicate);

//anyMatch():元素是否有任意一个匹配条件
boolean anyMatch(Predicate<? super T> predicate);

//nonMatch():元素是否全部都不满足条件
boolean noneMatch(Predicate<? super T> predicate);

使用:

List<Integer> list = new ArrayList<>();
    list.add(13);
    list.add(12);
    list.add(8);
    list.add(4);

    boolean b1 = list.stream().allMatch((n) -> {return n > 8;});//false
    boolean b2 = list.stream().anyMatch((n) -> {return n > 8;});//true
    boolean b3 = list.stream().noneMatch((n) -> {return n > 8;});//false

sorted

sorted()方法,对流中的数据进行排序

Stream<T> sorted();

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

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
}

使用:

List<Integer> list = new ArrayList<>();
        list.add(13);
        list.add(12);
        list.add(8);
        list.add(4);
        
        list.stream().sorted((str1,str2)->(str1-str2)).forEach(System.out::println);

count

用来计算元素的个数,返回一个long类型的值,代表元素的个数

long count();

使用:

    public static void main(String[] args) {
        long count = Stream.of("a", "b", "c", "d", "a", "b", "a", "c")
                .count();
        System.out.println("元素的个数为:"+count);
    }

peek

List<Person> lists = new ArrayList<Person>();
lists.add(new Person(1L, "p1"));
lists.add(new Person(2L, "p2"));
lists.add(new Person(3L, "p3"));
lists.add(new Person(4L, "p4"));
System.out.println(lists);

List<Person> list2 = lists.stream()
				 .filter(f -> f.getName().startsWith("p"))
                .peek(t -> {
                    System.out.println(t.getName());
                })
                .collect(Collectors.toList());
System.out.println(list2);

mapToInt

mapToInt()方法,将Stream流中的Integer类型的元素转换成int类型

IntStream mapToInt(ToIntFunction<? super T> mapper);

使用:

List<Integer> list = new ArrayList<>();
        list.add(12);
        list.add(8);
        list.add(4);
        list.add(13);
        
list.stream().mapToInt(new ToIntFunction<Integer>() {
            @Override
            public int applyAsInt(Integer value) {
                return value;
            }
        }).forEach(System.out::println);

1111.gif