java8新特性

Lambda表达式

有以下代码:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("新线程中执行的代码"+Thread.currentThread().getName());
    }
}).start();

代码分析:

  1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心;
  2. 为了指定run方法体,不得不需要Runnable的实现类;
  3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类;
  4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错;
  5. 而实际上,我们只在乎方法体中的代码。

 上面代码可以写成lambda形式如下:

new Thread(()->{
    System.out.println("lambda表达式新线程"+Thread.currentThread().getName());
}).start();

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

lambda运行就是将函数作为一个参数传递到方法中。用以简化接口的使用,让代码更加简洁

lambda的基础语法:

(参数类型 参数1,参数类型 参数2,...)->{

        代码体;

}

格式1:无参,无返回值

public interface UserService {
    void show();
}
public class Demo02Lambda {
    public static void main(String[] args) {
        goShow(new UserService() {
            @Override
            public void show() {
                System.out.println("show方法执行了");
            }
        });
        goShow(()->{
            System.out.println("lambda show方法执行了");
        });
    }
    public static void goShow(UserService userService){
        userService.show();
    }
}

格式2:有参且有返回值的

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private Integer age;
    private Integer height;
}
public class Demo03Lambda {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(new Person("zans",12,156));
        list.add(new Person("lis",13,170));
        list.add(new Person("ww",21,168));
        list.add(new Person("zl",32,163));
//        Collections.sort(list, new Comparator() {
//            @Override
//            public int compare(Person o1, Person o2) {
//                return o1.getAge()-o2.getAge();
//            }
//        });
        Collections.sort(list,((o1, o2) -> {
            return o1.getAge()-o2.getAge();
        }));
        for (Person person : list) {
            System.out.println(person);
        }
    }
}

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

  • 小括号内的参数类型可以省略;
  • 如果小括号内有且仅有一个参数,则小括号可以省略;
  • 如果大括号内有且仅有一个语句,可以同时省略大括号,return关键字及语句分号。

 例如下面例子:

public interface OrderService {
    String show(String name);
}
public interface StudentService {
    String show(String name,Integer age);
}
public class Demo04Lambda {
    public static void main(String[] args) {
        goStudent((String name,Integer age)->{
           return name+age+"666";
        });
        //省略写法
        goStudent((name,age)->name+age+"123");

        goOrder((String name)->{
            return name+"333";
        });
        //省略写法
        goOrder(name->name+"123");
    }
    public static void goStudent(StudentService studentService){
        studentService.show("sja",22);
    }
    public static void goOrder(OrderService orderService){
        orderService.show("lili");
    }
}

Lambda表达式的使用前提

Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意:

1.方法的参数或局部变量类型必须为接口才能使用Lambda

2.接口中有且仅有一个抽象方法(@Functionallnterface)

//@FunctionalInterface:被该注解修饰的接口只能有一个抽象方法
@FunctionalInterface
public interface UserService {
    void show();
}

Lambda和匿名内部类的对比

1.所需类型不—样

  • 匿名内部类的类型可以是类,抽象类,接口Lambda表达式需要的类型必须是接口

2.抽象方法的数量不—样

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

3.实现原理不—样

  • 匿名内部类是在编译后形成—个class
  • Lambda表达式是在程序运行的时候动忝生成class

函数式接口

如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。

我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。

Lambda表达式的本质:作为函数式接口的实例

Java8中关于Lambda表达式提供的4个基本的函数式接口(作用:在写lambda表达式的时候没有必要自己声明接口):

Consumer

Consumer(有参无返回值的接口),对于lambda表达式需要提供一个返回数据的类型,是用来消

费数据的,使用时需要指定一个泛型来定义参数类型。源码如下:

@FunctionalInterface
public interface Consumer {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
    
    default Consumer andThen(Consumer after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}

使用:

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

默认方法:andThen

如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时

候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default

方法andThen方法。

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

Function

Function(有参有返回值接口),Function接口是根据一个类型的数据得到另一个类型的数据,前

者称为前置条件,后者称为后置条件。源码如下:

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

使用:传递一个字符串,返回一个数字

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

    }
}

默认方法:andThen,也是用来进行组合操作,compose就是andthen反过来的效果,而静态方法

identity则是输入什么参数就返回什么参数 。

public class FunctionTest {
    public static void main(String[] args) {
        test(msg->{
            return Integer.parseInt(msg);
        },msg2->{
            return msg2 * 10;
        });
    }
    public static void test(Function function1,Function function2){
        Integer i2 = function1.andThen(function2).apply("88");
        Integer i2 = function2.compose(function1).apply("88");
        System.out.println("i2"+i2);
    }
}

Supplier

Supplier(无参有返回值类型,是用来生产数据的)

@FunctionalInterface
public interface Supplier {

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

使用:

import java.util.Arrays;
import java.util.function.Supplier;
public class SupplierTest {
    public static void main(String[] args) {
        fun1(()->{
            int arr[]={22,11,56,42,99,8};
            //计算出数组中的最大值
            Arrays.sort(arr);
            return arr[arr.length-1];
        });
    }
    private static void fun1(Supplier supplier){
        Integer max = supplier.get();
        System.out.println("max="+max);
    }
}

Predicate

Predicate(有参,返回值为Boolean型接口)

@FunctionalInterface
public interface Predicate {
    /**
     * 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);
    default Predicate and(Predicate other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
    default Predicate negate() {
        return (t) -> !test(t);
    }
    default Predicate or(Predicate other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    static  Predicate isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

使用:如果msg长度大于3,返回true

public class predicateTest {
    public static void main(String[] args) {
        test(msg->{
            return msg.length()>3;
        },"hahahha");
    }
    public static void test(Predicate predicate,String msg){
        boolean b = predicate.test(msg);
        System.out.println("b"+b);
    }
}

 在Predicate中的默认方法提供了逻辑关系操作 and or negate isEquals方法

public class predicateTest {
    public static void main(String[] args) {
        test(msg1->{
            return msg1.contains("H");
        },msg2->{
            return msg2.contains("W");
        });
    }
    public static void test(Predicate p1,Predicate p2){
       //P1包含H,同时p2包含w
        boolean pp1 = p1.and(p2).test("Hello");
        //P1包含H,或者p2包含w
        boolean pp2 = p1.or(p2).test("Hello");
        //p1不包含H
        boolean pp3 = p1.negate().test("Hello");
    }
}

方法引用

在Lambda表达式中要执行的代码和我们另一个方法中的代码是一样的,这时就没有必要重写一份

逻辑了,这时我们就可以“引用”重复代码。

public class FunctionRefTest02 {
    public static void main(String[] args) {
        printMax(FunctionRefTest02::getTotal);
    }
    public static void getTotal(int a[]){
        int sum=0;
        for (int i : a) {
            sum+=i;
        }
        System.out.println("数组织和:"+sum);
    }
    private static void printMax(Consumer consumer){
        int[] a = {10,20,30,40,50};
        consumer.accept(a);
    }
}

符号表示 ::

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

常见的引用方式:

方法引用在JDK8中使用是相当灵活的,有以下几种形式:

1. instanceName::methodName 对象::方法名

2. ClassName::staticMethodName 类名::静态方法

3. ClassName::methodName 类名::普通方法

4. ClassName::new 类名::new 调用的构造器

5. TypeName[]::new String[]::new 调用数组的构造器

方法引用的注意事项:

1. 被引用的方法,参数要和接口中的抽象方法的参数一样

2. 当接口抽象方法有返回值时,被引用的方法也必须有返回值

对象::方法名

这是最常见的一种用法。如果一个类中已经存在了一个成员方法,则可以通过对象名引用成员方法

public class FunctionRefTest3 {
    public static void main(String[] args) {
        Date now = new Date();
        Supplier supplier = ()->{return now.getTime();};
        System.out.println(supplier.get());
        //通过方法引用
        Supplier supplier1 =now::getTime;
        System.out.println(supplier1.get());
    }
}

 类名::静态方法名

public class FunctionRefTest4 {
    public static void main(String[] args) {
        Supplier supplier1 = ()->{
            return System.currentTimeMillis();
        };
        System.out.println(supplier1.get());
        //通过方法引用
        Supplier supplier2 = System::currentTimeMillis;
        System.out.println(supplier2.get());
    }
}

类名::引用实例方法

Java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数

作为方法的调用者。

public class FunctionRefTest5 {
    public static void main(String[] args) {
        Function function=(s)->{
            return s.length();
        };
        System.out.println(function.apply("hello"));
        //通过方法引用实现
        Function function2=String::length;
        System.out.println(function2.apply("hahahha"));
        BiFunction function3 = String::substring;
        System.out.println(function3.apply("hhhhhhhh", 3));
    }
}

类名::构造器

由于构造器的名称和类名完全一致,所以构造器引用使用 ::new 的格式使用

public class FunctionRefTest6 {
    public static void main(String[] args) {
        Supplier person1 = () -> {
            return new Person();
        };
        System.out.println(person1.get());
        //方法引用
        Supplier person2 = Person::new;
        System.out.println(person2.get());
        BiFunction function = Person::new;
        System.out.println(function.apply("zs", 22));
    }
}

数组::构造器 

public class FunctionRefTest7 {
    public static void main(String[] args) {
        Function fun = (len)->{
            return new String[len];
        };
        String[] strings = fun.apply(3);
        System.out.println("数组的长度:"+strings.length);
        //方法引用,调用数组的构造器
        Function fun1 = String[]::new;
        String[] strings1 = fun1.apply(5);
        System.out.println("数组的长度:"+strings1.length);
    }
}

Stream API

当我们在需要对集合中的元素进行操作的时候,除了必需的添加,删除,获取外,最典型的操作就

是集合遍历。针对与我们不同的需求总是一次次的循环,这时我们希望有更加高效的处理方式,就

可以通过JDK8中提供的Stream API来解决这个问题。

public class FunctionRefTest8 {
    public static void main(String[] args) {
        //定义一个集合
        List list = Arrays.asList("张三","张三丰","成龙","周星驰");
        // 1.获取所有 姓张的信息
        // 2.获取名称长度为3的用户
        // 3. 输出所有的用户信息
        list.stream().filter(s->s.startsWith("张"))
                .filter(s->s.length()==3)
                .forEach(System.out::println);
    }
}

注意:Stream和IO流(InputStream/OutputStream)没有任何关系。

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是

对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一

个原材料加工成一个商品。

Stream流的获取方式

根据conllection获取

首先,java.util.Collection 接口中加入了default方法 stream,也就是说Collection接口下的所有的实

现都可以通过steam方法来获取Stream流。

public class streamTest1 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        list.stream();
        HashSet set = new HashSet<>();
        set.stream();
        Vector vector = new Vector<>();
        vector.stream();
    }
}
 
  

但是Map接口别没有实现Collection接口,那这时怎么办呢?这时我们可以根据Map获取对应的key

value的集合。 

public class streamTest1 {
    public static void main(String[] args) {
        HashMap map = new HashMap<>();
        Stream stream = map.keySet().stream();
        Stream stream1 = map.values().stream();
        Stream> stream2 = map.entrySet().stream();
    }
} 
  

通过stream.of方法获取

在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所

有Stream接口中提供了静态方法of。

 

public class streamTest1 {
    public static void main(String[] args) {
        Stream a = Stream.of("a", "b", "c");
        String[] a1 =  {"aa","bb","cc"};
        Stream a11 = Stream.of(a1);
        Integer[] c1 = {1,2,3,4};
        Stream c11 = Stream.of(c1);
        //基本数据类型不能获取stream
        int[] d1 = {1,2,3,4};
        Stream.of(d1).forEach(System.out::println);
    }
}

Stream 常用方法介绍

java8新特性_第1张图片

终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和forEach 方法。

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

Stream注意事项(重要):

  •  Stream只能操作一次
  •  Stream方法返回的是新的流
  • Stream不调用终结方法,中间的操作不会执行

foreach

forEach用来遍历流中的数据,该方法接收一个Consumer接口,会将一个流元素交给函数处理

 void forEach(Consumer action);

public class streamTest1 {
    public static void main(String[] args) {
        Stream.of("a","s","d","f").forEach(System.out::println);
    }
}

count

tream流中的count方法用来统计其中的元素个数

long count();

public class streamTest1 {
    public static void main(String[] args) {
        long count = Stream.of("a", "s", "d", "f").count();
        System.out.println(count);
    }
}

filter

filter方法的作用是用来过滤数据的。返回符合条件的数据

Stream filter(Predicate predicate);

public class streamTest1 {
    public static void main(String[] args) {
        Stream.of("ab","scd" ,"sz2", "ded", "f2")
                .filter(s->s.length()>2)
                .filter(s->s.startsWith("s"))
                .filter(s->s.contains("z")).forEach(System.out::println);
    }
}

 limit

Stream limit(long maxSize);

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

skip

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

Stream skip(long n);

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

map

如果我们需要将流中的元素映射到另一个流中,可以使用map方法:

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

public class streamTest1 {
    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);
    }
}

sorted

Stream sorted();

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

distinct

Stream distinct();

public class streamTest1 {
    public static void main(String[] args) {
        Stream.of("1","2" ,"2","4","1")
               .distinct()
                .forEach(System.out::println);
        Stream.of(
                new Person("zs",21)
                ,new Person("lis",21)
                ,new Person("zs",21)
        ).distinct().forEach(System.out::println);
    }
}

Stream流中的distinct方法对于基本数据类型是可以直接去重的,但是对于自定义类型,我们是需

要重写hashCode和equals方法来移除重复元素。

match

如果需要判断数据是否匹配指定的条件,可以使用match相关的方法

boolean anyMatch(Predicate super T> predicate); // 元素是否有任意一个满足条件

boolean allMatch(Predicate super T> predicate); // 元素是否都满足条件

boolean noneMatch(Predicate super T> predicate); // 元素是否都不满足条件

public class streamTest1 {
    public static void main(String[] args) {
        boolean b = Stream.of("1", "4", "5", "3", "6")
                .map(Integer::parseInt)
//                .anyMatch(s->s>3)
//                .allMatch(s->s>0)
                .noneMatch(s -> s > 7);
        System.out.println(b);
    }
}

find

Optional findFirst();

Optional findAny();

public class streamTest1 {
    public static void main(String[] args) {
        Optional first = Stream.of("1", "4", "5", "3", "6")
                .findFirst();
        System.out.println(first.get());
        Optional any = Stream.of("1", "4", "5", "3", "6").findAny();
        System.out.println(any.get());
    }
}

max和min

Optional min(Comparator super T> comparator);

Optional max(Comparator super T> comparator);

public class streamTest1 {
    public static void main(String[] args) {
        Optional max = Stream.of("1", "4", "5", "3", "6")
                .map(Integer::parseInt)
                .max((o1, o2) -> o1 - o2);
        System.out.println(max.get());
        Optional min = Stream.of("1", "4", "5", "3", "6")
                .map(Integer::parseInt).min((o1, o2) -> o1 - o2);
        System.out.println(min.get());
    }
}

reduce

归并计算,为终端操作,可以对中间操作筛选出来的流数据作出一系列的加减乘除等操作。

T reduce(T identity, BinaryOperator accumulator); 

public class streamTest1 {
    public static void main(String[] args) {
        Integer reduce = Stream.of("1", "4", "5", "3", "6")
                .map(Integer::parseInt)
                .reduce(0, Integer::sum);
        System.out.println(reduce);
        Integer reduce1 = Stream.of("1", "4", "5", "3", "6")
                .map(Integer::parseInt)
                .reduce(0, (x, y) -> x > y ? x : y);
        System.out.println(reduce1);
    }
}

reduce和map组合 

public class streamTest1 {
    public static void main(String[] args) {
        // 1.求出所有年龄的总和
        Optional reduce = Stream.of(
                new Person("zs", 12)
                , new Person("zs", 23)
                , new Person("zs", 25)
        ).map(Person::getAge).reduce(Integer::sum);
        System.out.println(reduce.get());
        // 1.求出所有年龄的最大值
        Optional reduce1 = Stream.of(
                new Person("zs", 12)
                , new Person("zs", 23)
                , new Person("zs", 25)
        ).map(Person::getAge).reduce(Math::max);
        System.out.println(reduce1.get());
        //统计字符a出现的次数
        Integer total = Stream.of("a", "a", "a", "b", "b", "c").map(s -> "a".equals(s) ? 1 : 0).reduce(0, Integer::sum);
        System.out.println(total);
    }
}

concat 

public class streamTest1 {
    public static void main(String[] args) {
        Stream stream1 = Stream.of("1", "2", "3");
        Stream stream2 = Stream.of("4", "5", "6");
        Stream.concat(stream1,stream2).forEach(System.out::println);
    }
}

mapToInt

如果需要将Stream中的Integer类型转换成int类型,可以使用mapToInt方法来实现

public class streamTest1 {
    public static void main(String[] args) {
      Integer arr[] = {1,2,3,4,5};
        IntStream toInt = Stream.of(arr).mapToInt(Integer::intValue);
        System.out.println(toInt);
    }
}

你可能感兴趣的:(java)