Lambda表达式语法及应用

Lambda表达式语法及应用

之前在Mybatis-plus中一直有用到Lambda表达式的用法,就找了一些教程具体学习了一下,做个记录。

Demo地址

Lambda简单比较

传统模式下创建新线程

new Thread(new Runnable() {
	@Override
	public void run() {
		System.out.println("threading..." + Thread.currentThread().getId());
	}
}).start();

Lambda表达式创建新线程

new Thread(() -> {
	System.out.println("lambda threading..." + Thread.currentThread().getId());
});

函数式接口

Lambda的核心是就是一个函数式接口 ,函数式接口是Java类型系统中的特殊接口,语义检测注解:@FunctionalInterface,Lambda只能操作一个方法

案例

首先创建一个函数式接口

@FunctionalInterface
public interface UserCredential {

    /**
     * 校验用户信息
     *
     * @param username
     * @return
     */
    String verifyUser(String username);

    default String getCredential(String username) {
        if ("admin".equals(username)) {
            return "default 系统管理员";
        } else if ("manager".equals(username)) {
            return "default 用户管理员";
        } else {
            return "default 普通会员";
        }
    }
}

接口实现类

public class UserCredentialImpl implements UserCredential {
    /**
     * 校验用户信息
     *
     * @param username
     * @return
     */
    @Override
    public String verifyUser(String username) {
        if ("admin".equals(username)) {
            return "系统管理员";
        } else if ("manager".equals(username)) {
            return "用户管理员";
        } else {
            return "普通会员";
        }
    }
}

调用测试

public static void main(String[] args) {
    	// 简单实现
        UserCredential user = new UserCredentialImpl();
        System.out.println(user.verifyUser("admin"));
        System.out.println(user.getCredential("manager"));

        // 匿名内部类,实现接口的抽象方法
        UserCredential ic = new UserCredential() {
            @Override
            public String verifyUser(String username) {
                return "admin".equals(username) ? "管理员" : "会员";
            }
        };
        System.out.println(ic.verifyUser("manager"));
        System.out.println(ic.verifyUser("admin"));

        // lambda 表达式,针对函数式接口的简单实现
        UserCredential ic2 = (String username) -> {
            return "admin".equals(username) ? "lambda管理员" : "lambda会员";
        };
        System.out.println(ic2.verifyUser("admin"));
        System.out.println(ic2.verifyUser("manager"));
}

常见的函数式接口

接收参数T,返回boolean
Predicate<String> pre = (String username) -> {
	return "admin".equals(username);
};
System.out.println(pre.test("admin"));
System.out.println(pre.test("manager"));
接收参数T,没有返回值
Consumer<String> con = (String message) -> {
	System.out.println("发送的消息是:" + message);
	System.out.println("发送完毕");
};
con.accept("nice");
con.accept("CseaNB");
接收参数T对象,返回R对象
Function<String, Integer> fun = (String gender) -> {
	return "male".equals(gender) ? 1 : 0;
};
System.out.println(fun.apply("male"));
System.out.println(fun.apply("female"));
不接收任何参数,直接通过get()获取指的类型对象
Supplier<String> sup = () -> {
	return UUID.randomUUID().toString();
};
System.out.println(sup.get());
System.out.println(sup.get());
System.out.println(sup.get());
接收参数对象T,返回结果对象T
UnaryOperator<String> uo = (String img) -> {
	img += "[100*200]";
	return img;
};
System.out.println(uo.apply("原图--"));
接收两个T对象,返回一个T对象结果
BinaryOperator<String> bo = (String firstname, String lastname) -> {
	firstname += "Csea";
	lastname += "NB";
	return firstname + lastname;
};
System.out.println(bo.apply("first", "last"));

Lambda表达式的基本语法

声明:和Lambda表达式绑定的接口类型

参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致

操作符:->

执行代码块:包含在一对大括号中,出现在操作符的右侧

接口声明:(参数) -> {执行代码块}

案例

没有参数,没有返回值的Lambda表达式绑定的接口

interface ILambda {
	void test();
}

ILambda iLambda1 = () -> {
	System.out.println("Csea");
	System.out.println("NB");
};
iLambda1.test();

带有参数,没有返回值的Lambda表达式

interface ILambda2 {
        void test(String name, int age);
    }

		// 如果执行代码块只有一行,可以省略{}
        ILambda iLambda2 = () -> System.out.println("iLambda2");
        iLambda2.test();

        ILambda2 iLambda21 = (String n, int a) -> {
            System.out.println("name:" + n + " age " + a);
        };
        iLambda21.test("Csea", 22);

        // 这里Lambda会自动识别参数类型
        ILambda2 iLambda22 = (n, a) -> {
            System.out.println("name: " + n + " age " + a);
        };
        iLambda22.test("Csea", 22);

带有参数,带有返回值的Lambda表达式

interface ILambda3 {
	Integer test(Integer x, Integer y);
}

 		ILambda3 iLambda3 = (x, y) -> {
            return x + y;
        };
        System.out.println(iLambda3.test(1, 2));

        // 如果只有一行代码块,还可以省略return
        ILambda3 iLambda31 = (x, y) -> x + y;
        System.out.println(iLambda31.test(3, 3));

小结:

  1. lambda表达式,必须和接口绑定;
  2. Lambda表达式的参数,可以附带0个到n个参数,括号在的参数类型可以不用指定,JVM在运行时,会自动根据绑定的抽象方法中的自动推导;
  3. lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回;
  4. 若果添加了大括号,或者有多行代码,必须通过return关键词返回结果。

Lambda访问变量

public class App2 {
    String s1 = "全局变量";

    public void testInnerClass() {
        String s2 = "局部变量";

        new Thread(new Runnable() {
            String s3 = "内部变量";

            @Override
            public void run() {
                System.out.println(s1);
                System.out.println(s2);//不能对局部变量进行修改(final)
                System.out.println(s3);
                System.out.println(this.s3);
            }
        }).start();
    }

    public void lambdatest() {
        String s2 = "局部lambda";
        new Thread(() -> {
            String s3 = "内部lambda";

            System.out.println(this.s1); //this关键词,表示的是所属方法所在的对象
            System.out.println(s2);//不能对局部变量进行修改(final)
            System.out.println(s3);
        }).start();
    }

    public static void main(String[] args) {
        App2 app2 = new App2();
        app2.testInnerClass();

        app2.lambdatest();

        // lambda变量的访问操作优化了匿名类内部的this关键字,不再单独建立对象作用域
        // 表达式本身就是所属类型对象的一部分,在语法语义上更加简洁
    }
}

Lambda类型检查

public class App3 {

    public static void test(MyInterface<String, List> inter) {
        List<String> list = inter.strategy("hello", new ArrayList());
        System.out.println(list);
    }


    public static void main(String[] args) {
        test(new MyInterface<String, List>() {
            @Override
            public List strategy(String s, List list) {
                list.add(s);
                return list;
            }
        });

        test((x, y) -> {
            y.add(x);
            return y;
        });
        /**
         * (x,y) -> {...} --> test(param) --> param==MyInterface --> lambda表达式 -> MyInterface类型
         * 这个就是对于lambda表达式的类型检查,MyInterface接口就是lambda表达式的目标类型(target typing)
         *
         *(x,y) -> {...}  --> MyInterface.strategy(T t,R r) --> MyInterface<String,List> inter
         * --> T == String R==List --> lambda --> (x,y) == strategy(T t,R r) --> x == T == String  y==String==List
         * lambda表达式参数的类型检查
         */
    }

    @FunctionalInterface
    interface MyInterface<T, R> {
        R strategy(T t, R r);
    }
}

Lambda方法重载

public class App4 {

    interface Param1 {
        void outInfo(String info);
    }

    interface Param2 {
        void outInfo(String info);
    }

    public void lambdaMethod(Param1 param1) {
        param1.outInfo("Hello Csea");
    }

    public void lambdaMethod(Param2 param2) {
        param2.outInfo("Hi Csea");
    }

    public static void main(String[] args) {
        App4 app4 = new App4();

        app4.lambdaMethod(new Param1() {
            @Override
            public void outInfo(String info) {
                System.out.println(info);
            }
        });

        /**
         * lambda存在类型检查 -> 自动推到lambda表达式目标类型
         * lambdaMethod() -> 方法 -> 重载方法
         *      -> Param1 函数式接口
         *      -> Param2 函数式接口
         *      调用方法 -> 传递Lambda表达式 -> 自动推导
         *          -> Param1 |  Param2
         */

        /**
         * 这里抛红是因为 他对两个lambda表达式去进行了匹配
         * 在某情况下限制了传统语法结构中功能操作
         * 如果在出现方法重载的类型中,参数都是函数式接口的情况,请使用匿名内部类的实现替代lambda
         */
//        app4.lambdaMethod((String info) -> {
//            System.out.println(info);
//        });
    }
}

Lambda表达式在集合中的运用

public class Test {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("tom", "男", 16));
        personList.add(new Person("jerry", "男", 18));
        personList.add(new Person("mary", "男", 24));
        personList.add(new Person("tony", "男", 30));
        personList.add(new Person("csea", "男", 22));
        personList.add(new Person("spack", "男", 50));


//        匿名内部类实现方式
        Collections.sort(personList, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        System.out.println(personList);

//        lambda表达式 实现
        Collections.sort(personList, (p1, p2) -> p1.getAge() - p2.getAge());
        System.out.println(personList);

//        静态方法引用
        Collections.sort(personList, Person::compareByAge);
        System.out.println(personList);

//        实例方法引用方式
        PersonUtil personUti = new PersonUtil();
        Collections.sort(personList, personUti::compareByName);
        System.out.println(personList);

//        构造方法引用:函数式接口使用
        IPerson ip = Person::new;
        Person person = ip.initPerson("tom", "男", 22);
        System.out.println(person);

    }

}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
    private String name;
    private String gender;
    private int age;

    public static int compareByAge(Person person1, Person person2) {
        return person1.getAge() - person2.getAge();
    }
}

class PersonUtil {
    public int compareByName(Person p1, Person p2) {
        return p1.getName().hashCode() - p2.getName().hashCode();
    }
}

interface IPerson {
    Person initPerson(String name, String gender, int age);
}

引入Stream API与其他方式对比

public class Test2 {
    public static void main(String[] args) {
        // 存储账号列表
        List<String> accounts = new ArrayList<>();
        accounts.add("tom");
        accounts.add("jerry");
        accounts.add("csea");
        accounts.add("shuke");
        accounts.add("spack");

        // 长度大于5才是有效账号
        for (String account : accounts) {
            if (account.length() >= 5) {
                System.out.println("有效账号" + account);
            }
        }

//        迭代器操作
        Iterator<String> it = accounts.iterator();
        while (it.hasNext()) {
            String account = it.next();
            if (account.length() >= 5) {
                System.out.println("有效账号" + account);
            }
        }

//        steam结合lambda表达式 进行处理
//        先获取到accounts列表,然后通过 过滤 拿到长度 >= 5的数据,再把它转换成一个list
        List validAccounts = accounts.stream().filter(s -> s.length() >= 5).collect(Collectors.toList());
        System.out.println(validAccounts);
    }
}

Stream概述

Stream 是一个聚合操作

stream处理流程:

  • 数据源
  • 数据转换
  • 获取结果

获取stream对象

  • 1.从集合或者数组中获取

    Collection.stream()

    Collection.parallelStream()

    Array.stream(T t)

    1. BufferReader

    BufferReder.lines() -> stream()

    1. 静态工厂

    java.util.stream.IntStream.range…

    java.nio.file.Files.walk()…

    1. 自定构建

    java.util.Spliterator

    1. 更多方式…

    Random.ints()

    Pattern.splitAsStream()…

中间操作API{intermediate}

操作结果是一个Stream,中间操作可以有一个或多个连续的中间操作,需要注意的是,中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。

中间操作过程:

  • 无状态,数据处理时,不受前置中间操作的影响

    map/filter/peek/parallel/sequential/unordered

  • 有状态:数据处理时,受前置中间操作的影响

    distinct/sorted/limit/skip

终结操作,结束操作{Terminal}

需要注意:一个Stream对象,只有一个Terminal操作,这个操作一旦发生,就会真实处理数据,生成对应的处理结果(不可逆)

终结操作:

  • 非短路操作,当前的Stream对象必须处理完集合中所有的数据,才能得到处理结果

    forEach/forEachOrdered/toArray/reduce/collect/min/max/cout/iterator

  • 短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果

    anyMatch/allMatch/noneMatch/findFirst/findAny

    对于无限大的Stream -> 有限大的Stream

Stream操作集合

集合的简单操作

public class Test1 {
    public static void main(String[] args) {
        //    批量数据 转换 stream对象
        Stream stream1 = Stream.of("admin", "tom", "damu");

        //  数组
        String[] strArray = new String[]{"xxxx", "yyyy"};
        Stream stream2 = Arrays.stream(strArray);

        // 列表
        List<String> list = new ArrayList<>();
        list.add("苹果");
        list.add("西瓜");
        list.add("香蕉");
        list.add("菠萝");
        list.add("樱桃");
        Stream stream3 = list.stream();

        // 集合
        Set<String> set = new HashSet<>();
        set.add("华硕");
        set.add("Apple");
        set.add("联想");
        Stream stream4 = set.stream();

        // Map
        Map<String, Integer> map = new HashMap<>();
        map.put("tom", 100);
        map.put("jerry", 200);
        map.put("csea", 10000);
        Stream stream5 = map.entrySet().stream();

        // Stream 对象 对于基本数据类型的功能封装
        // int long  double
        IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
        IntStream.range(1, 5).forEach(System.out::println);// 包含起始数据,不包含结束数据
        IntStream.rangeClosed(1, 5).forEach(System.out::println);// 包含起始数据,包含结束数据

        // Stream对象 --> 转换得到 指定的数据类型,stream 一次终端操作之后就不能再次操作了
        // 数组
        Object[] objx = stream1.toArray(String[]::new);

        // 字符串
        String str = stream1.collect(Collectors.joining()).toString();

        // 列表
        List<String> listx = (List<String>) stream1.collect(Collectors.toList());

        // 结合
        Set<String> setx = (Set<String>) stream1.collect(Collectors.toSet());

        // map
        Map<String, Object> mapx = (Map<String, Object>) 				      					stream1.collect(Collectors.toMap(x -> x, y -> "value:" + y));

    }

}

常见Stream API操作

public class Test1 {
    public static void main(String[] args) {
        // stream中常见的API操作
        List<String> accountList = new ArrayList<>();
        accountList.add("xxxx");
        accountList.add("yyyyyy");
        accountList.add("zzzz");
        accountList.add("aaaaaa");

        // map中间操作,map()方法接收一个Function接口
        accountList = accountList.stream().map(x -> "梁山好汉" + x).collect(Collectors.toList());

        // filter() 添加过滤条件,过滤符合条件的用户
        accountList = accountList.stream().filter(x -> x.length() >= 5).collect(Collectors.toList());

        // forEach() 增强型循环
        accountList.forEach(x -> System.out.println("ForEach-> " + x));

        // peek() 中间操作,迭代数据完成数据的依次处理过程
        accountList.stream()
                .peek(x -> System.out.println("peek1 :" + x))
                .peek(x -> System.out.println("peek 2:" + x))
                .forEach(System.out::println);

        accountList.forEach(System.out::println);

        // stream对于数字运算的支持
        List<Integer> initList = new ArrayList<>();
        initList.add(1);
        initList.add(2);
        initList.add(22);
        initList.add(88);
        initList.add(22);

        // skip() 中间操作,有状态,跳过部分数据
        initList.stream().skip(3).forEach(System.out::println);

        // limit() 中间操作,有状态,限制输出数据量
        initList.stream().skip(3).limit(2).forEach(System.out::println);

        // distinct()中间操作,有状态,剔除重复的数据
        initList.stream().distinct().forEach(System.out::println);

        // sorted中间操作,有状态,排序
        initList.stream().sorted();
        // max() 获取最大值
        initList.stream().max((x, y) -> x - y);
        // min() 获取最小值
        // reduce()合并处理数据
        Optional optional = initList.stream().reduce((sum, y) -> sum + y);
    }

}

性能对比——简单数据处理

因为不同的机器跑出来的数据也不同,这里我就不贴我的运行时间了,如果感兴趣就把代码copy一下到自己的机器上运行。

public class Test2 {
    public static void main(String[] args) {
        Random random = new Random();

        List<Integer> integerList = new ArrayList<>();

        for (int i = 0; i < 1000000; i++) {
            integerList.add(random.nextInt(Integer.MAX_VALUE));
        }

        // 1.stream
        testStream(integerList);
        // 2.parallelStream
        testParallelStream(integerList);
        // 3.普通for
        testForLoop(integerList);
        // 4.增强型for
        testStrongForLoop(integerList);
        // 5. 迭代器
        testInterator(integerList);
    }

    public static void testStream(List<Integer> list) {
        long start = System.currentTimeMillis();
        Optional optional = list.stream().max(Integer::compare);
        System.out.println(optional.get());
        long end = System.currentTimeMillis();
        System.out.println("testStream:" + (end - start) + "ms");
    }

    public static void testParallelStream(List<Integer> list) {
        long start = System.currentTimeMillis();
        Optional optional = list.parallelStream().max(Integer::compare);
        System.out.println(optional.get());
        long end = System.currentTimeMillis();
        System.out.println("testParallelStream:" + (end - start) + "ms");
    }

    public static void testForLoop(List<Integer> list) {
        long start = System.currentTimeMillis();
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < list.size(); i++) {
            int current = list.get(i);
            if (current > max) {
                max = current;
            }
        }
        System.out.println(max);
        long end = System.currentTimeMillis();
        System.out.println("testForLoop:" + (end - start) + "ms");
    }

    public static void testStrongForLoop(List<Integer> list) {
        long start = System.currentTimeMillis();
        int max = Integer.MIN_VALUE;
        for (Integer integer : list) {
            if (integer > max) {
                max = integer;
            }
        }
        System.out.println(max);
        long end = System.currentTimeMillis();
        System.out.println("testStrongForLoop:" + (end - start) + "ms");
    }

    public static void testInterator(List<Integer> list) {
        long start = System.currentTimeMillis();
        Iterator<Integer> it = list.iterator();
        int max = it.next();
        while (it.hasNext()) {
            int current = it.next();
            if (current > max) {
                max = current;
            }
        }
        System.out.println(max);
        long end = System.currentTimeMillis();
        System.out.println("testInterator:" + (end - start) + "ms");
    }
}

性能对比——复杂数据:对象

public class Test3 {

    public static void main(String[] args) {
        Random random = new Random();
        // 复杂数据类型:对象
        List<Product> productList = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            productList.add(new Product("pro" + i, i, random.nextInt(Integer.MAX_VALUE)));
        }

        // 1.stream
        testStream(productList);
        // 2.parallelStream
        testParallelStream(productList);
        // 3.普通for
        testForLoop(productList);
        // 4.增强型for
        testStrongForLoop(productList);
        // 5. 迭代器
        testInterator(productList);
    }

    public static void testStream(List<Product> list) {
        long start = System.currentTimeMillis();
        Optional optional = list.stream().max((p1, p2) -> p1.hot - p2.hot);
        System.out.println(optional.get());
        long end = System.currentTimeMillis();
        System.out.println("testStream:" + (end - start) + "ms");
    }

    public static void testParallelStream(List<Product> list) {
        long start = System.currentTimeMillis();
        Optional optional = list.parallelStream().max((p1, p2) -> p1.hot - p2.hot);
        System.out.println(optional.get());

        long end = System.currentTimeMillis();
        System.out.println("testParallelStream:" + (end - start) + "ms");
    }

    public static void testForLoop(List<Product> list) {
        long start = System.currentTimeMillis();
        Product max = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i).hot > max.hot) {
                max = list.get(i);
            }
        }
        System.out.println(max);

        long end = System.currentTimeMillis();
        System.out.println("testForLoop:" + (end - start) + "ms");
    }

    public static void testStrongForLoop(List<Product> list) {
        long start = System.currentTimeMillis();
        Product max = list.get(0);
        for (Product product : list) {
            if (product.hot > max.hot) {
                max = product;
            }
        }
        System.out.println(max);
        long end = System.currentTimeMillis();
        System.out.println("testStrongForLoop:" + (end - start) + "ms");
    }

    public static void testInterator(List<Product> list) {
        long start = System.currentTimeMillis();
        Iterator<Product> it = list.iterator();
        Product max = it.next();
        while (it.hasNext()) {
            Product current = it.next();
            if (current.hot > max.hot) {
                max = current;
            }
        }
        System.out.println(max);
        long end = System.currentTimeMillis();
        System.out.println("testInterator:" + (end - start) + "ms");
    }

    @AllArgsConstructor
    static
    class Product {
        String name;
        Integer stock;
        Integer hot;
    }
}

小结:

  1. 对于简单数据的处理,可以通过外部迭代来操作;
  2. 如果对于性能有一定的要求,可以使用并行stream来操作;
  3. 对于复杂对象处理的操作,stream的串行操作和普通的迭代相差无几,甚至超过普通迭代,完全可以简洁的stream来代替普通迭代。

线程安全

public class Test4 {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list1.add(i);
        }

        // 串行stream
        List<Integer> list2 = new ArrayList<>();
        list1.stream().forEach(x -> list2.add(x));
        System.out.println(list1.size());
        System.out.println(list2.size());
        // 并行stream
        List<Integer> list3 = new ArrayList<>();
        list1.parallelStream().forEach(x -> list3.add(x));
        System.out.println(list3.size());

        /**
         * 并行会数据丢失,因为当前操作的线程集合并不是安全的,所以多个线程访问共享数据出现了冲突,
         * 这个在redis中也有存在(特别是秒杀系统),可以添加redis分布式锁解决
         * 在官方的文档中说明:由于并行stream中使用Collections本身就不是线程安全,所以会由于多线程情况下导致数据不一致
         * Collections有提供自身的线程同步块,但是使用这些线程同步块可能存在线程竞争的问题
         * 如果想避免线程竞争的问题   operations和parallelStream能够让我们得到一个 在非线程安全的情况下 数据处理过程
         *  这个前提是 我们在操作的过程中,对于非线程安全中的数据不能修改
         */
        //解决list.parallelStream()并行流线程安全的方式:
        // 自定义编码,添加线程锁的方式,或者stream API中的线程安全的终端安全操作完成执行过程
        List<Integer> list4 = list1.parallelStream().collect(Collectors.toList());
    }
}

总结

个人觉得Lambda在代码上能一定的帮助我们进行优化,减少代码量,但是有些场景并不适合采用Lambda表达式,毕竟代码还是要一直维护的,Lambda表达式简化了代码表达,但是可读性也降低了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值