Java8新特性——lambda表达式

什么是lambda表达式?

Lambda 表达式是Java 8 的新特性,是一种新的编程语法。lambda语义简洁明了,性能良好,是Java 8 的一大亮点。废话不多说,我们来看个例子。

从内部类到lambda

lambda简化了内部类的使用,说起内部类,我第一个想到的就是创建一个线程:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("hello anonymity class.");
    }
});

想必大家对这段代码都不陌生,在jdk1.8之前,我们可以使用内部类便捷的创建一个线程,并指定执行内容,这里打印了"hello anonymity class."。接着我们看下lambda的写法:

Thread thread = new Thread(() -> System.out.println("hello anonymity class."));

是不是简化了好多,从之前的“怎么做”到现在的“做什么”,我们不用再手动重写run方法,只需要写这个Thread要做什么事情就可以了。在Thread方法参数中,空括号 () 代表没有参数,打印语句就是要执行的方法体,而 -> 则是分割参数与方法体的符号。
我们再来看个例子,选取文件夹内的隐藏文件:

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
    @Override
    public boolean accept(File pathname) {
        return pathname.isHidden();
    }
});

lambda:

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

这里lambda表达式用到了类、方法和两个冒号“::”。当我们已经有了isHidden方法时,就可以用Java8 的方法引用:: 语法把方法当做参数传给listFiles()。这就叫做 函数式编程 ,即把方法(函数)当做参数来传递

这时你可能会有疑问,这lambda表达式究竟是怎么实现的呢?我们来跟一下代码。

函数式接口

Thread类有很多个构造方法,如何确定new Thread(() -> System.out.println("hello anonymity class."))这个用的是哪个构造方法?
我们可以开发工具可以轻松定位到上面所调用的Thread的构造方法,是

public Thread(Runnable target) {
	init(null, target, "Thread-" + nextThreadNum(), 0);
}

一个入参为Runnable的方法。为什么偏偏是这个,而不是Thread(String name)呢。这里要引入一个概念,函数式接口
函数式接口与普通接口的唯一不同点是多了@FunctionalInterface注解。标注这个接口可以用作函数式接口。Runnable就被标注为函数式接口,只有一个抽象方法:public abstract void run();入参为空,且无返回。同样的,listFiles(FileFilter filter)的参数FileFilter也是一个函数式接口,它也有一个方法:boolean accept(File pathname);入参为File对象,返回一个boolean类型

所以lambda表达式是以入参+返回值类型来匹配函数式接口的, "->"号左边为入参(多个参数用括号包裹),右边是与接口方法的返回类型相同的方法体(多行用大括号包裹)

JDK已经为我们提供了现成的函数式接口(当然我们也可以自己写),在java.util.function包下,有了这些扩展,lambda才能展现它真正的魅力。

下面我们看一些实例

  1. 对列表迭代(Consumer)
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 9, 8, 7, 6);
list.forEach(e -> System.out.print(e + " "));
//1 2 3 4 5 9 8 7 6 
  1. 对列表排序(Comparator)
//打乱顺序
Collections.shuffle(list);
System.out.println(list);
//[2, 7, 5, 6, 9, 1, 8, 4, 3]
list.sort(Integer::compare);
System.out.println(list);
//[1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. 过滤偶数(Predicate)
    Predicate有一个抽象方法boolean test(T t);入参为T,返回boolean。我们可以用它来做过滤操作
Predicate<Integer> p = i -> i % 2 == 0;
List<Integer> result = predicateFilter(list, p);
System.out.println(result);
//[2, 4, 6, 8]

public static  <T> List<T> predicateFilter(List<T> list, Predicate<T> p) {
	List<T> result = new ArrayList<>();
	for (T t : list) {
		if (p.test(t)) {
			result.add(t);
		}
	}
	return result;
}
  1. 循环处理(Consumer)
    Consumer有一个抽象方法void accept(T t);入参为T,无返回。
Consumer<Integer> c = System.out::println;
consumerForEach(list, c);

public static <T> void consumerForEach(List<T> list, Consumer<T> c) {
	for (T t : list) {
		c.accept(t);
	}
}
/*1
* 2
* 3
* 4
* 5
* 6
* 7
* 8
* 9
*/   
  1. 获取商品的id(Function)
    Function有一个抽象方法R apply(T t);入参为T,返回R类型,用处更加广泛。
List<Product> productList = Arrays.asList(
        new Product(1L, "西红柿"),
        new Product(2L, "绵白糖"),
        new Product(3L, "黄瓜"),
        new Product(4L, "大蒜")
);
List<Long> productIds = productFunction(productList, Product::getId);

public static <T, R> List<R> productFunction(List<T> list, Function<T, R> f) {
    List<R> result = new ArrayList<>();
    for (T t : list) {
        result.add(f.apply(t));
    }
    return result;
}
/*
* 4-大蒜
* 2-绵白糖
* 1-西红柿
* 3-黄瓜
*/
  1. 根据商品的权重排序(Comparator)
    即便Comparator有多个抽象方法,lambda表达式一样可以匹配的到。

匹配了int compare(T o1, T o2);方法

productList.sort((p1, p2) -> p1.getSortWeight().compareTo(p2.getSortWeight()));

Comparator还重载了compare方法,入参是一个Function<? super T, ? extends U>

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }

所以我们还可以调用这个方法来实现排序

productList.sort(Comparator.comparing(Product::getSortWeight));

Java8中常用的函数式接口

函数式接口函数描述符
Predicate<T>T -> boolean
Consumer<T>T -> void
Function<T,R>T -> R
Supplier<T>() -> T
UnaryOperator<T>T -> T
BinaryOperator<T>(T, T) -> T
BiPredicate<L, R>(L, R) -> boolean
BiConsumer<T, U>(T, U) -> void
BiFunction<T, U, R>(T, U) -> R

lambda及函数式接口的例子

使用案例lambda的例子对应的接口函数
布尔表达式(List<String> list) -> list.empty()Predicate<List<String>>
创建对象() -> new Product(1L, “西红柿”)Supplier<Product>
消费对象(Product p) -> System.out.println(a.getId())Consumer<Product>
从一个对象中提取/选择(String s) -> s.length()Function<String, Integer>
合并两个值(int a, int b) -> a * bIntBinaryOperator
比较两个对象(Product p1, Product p2) -> p1.getSortWeight().compareTo(p2.getSortWeight())Comparator<Product>或 BiFunction<Product, Product, Integer>

函数式数据处理——Stream API

(stream)是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码了!

我们来看个例子体会一下。
同样是提取商品id,现在我们不再需要调用productFunction()方法了。

List<Long> productIdList = productList.stream().map(Product::getId).collect(Collectors.toList());
  • stream()获取productIdList的流
  • map()把流中的Product映射成Long
  • collect(Collectors.toList())从流中收集内容生成新的List

中间操作和终端操作

  • 在使用流时,会有两步操作,map()对流进行操作,称之为中间操作collect()对流进行整合,称之为终端操作
    stream提供的操作方法:
    中间操作
操作类型返回类型操作参数函数描述符作用
filter中间Stream<T>Predicate<T>T -> boolean筛选
map中间Stream<T>function<T,R>T -> R映射
limit中间Stream<T>数量限制
sorted中间Stream<T>Comparator<T>(T,T) -> int排序
distinct中间Stream<T>去重
flatMap中间Stream<R>Function<T, R>T -> Stream<R>平铺(展开)映射
peek中间Stream<T>ConsumerT -> void迭代
skip中间Stream<T>跳过
builder中间Builder<T>Builder<T>
empty中间Stream<T>() -> Stream<T>创建一个空流
of中间Stream<T>T -> Stream<T>创建包含T的流
iterate中间Stream<T>UnaryOperator<T>(T, UnaryOperator<T>) -> Stream<T>以一个初始值和迭代规则创建一个T流
generate中间Stream<T>Supplier<T>() -> Stream<T>创建一个流
concat中间Stream<T>(Stream<T>, Stream<T>) -> Stream<T>合并两个流

终端操作

操作类型返回类型作用
forEach终端void消费流中的每个元素并对其应用Lambda。这一操作返回void
count终端long返回流中元素的个数。这一操作返回long
collect终端R把流归约成一个集合,比如List、Map甚至是Integer。
toArray终端T[]把流归约成一个数组
reduce终端T规约函数,以一个初始值开始,按照规则计算
min终端Optional<T>获取最小值
max终端Optional<T>获取最大值
anyMatch终端boolean流中的任意一个对象满足条件
allMatch终端boolean流中的所有对象满足条件
noneMatch终端boolean流中的所有对象都不满足条件
findFirst终端Optional<T>返回第一个对象
findAny终端Optional<T>返回任意一个对象

来一些具体的使用实例:
村头的老王最近包地赚了些钱,开了一个小超市主要经营了下面这几类商品

/**
 * 商品种类
 */
enum GType {
    //水果
    FRUITS(1),
    //啤酒
    BEER(2),
    //香烟
    SMOKE(3),
    //肉类
    MEAT(4),
    //零食
    SNACKS(5),
    ;

    GType(int goodsType) { this.goodsType = goodsType; }
    
    private int goodsType;
    public int getGoodsType() { return goodsType; }
    public void setGoodsType(int goodsType) { this.goodsType = goodsType; }
}

然后老王进了一批货

	//商品
    class Goods {
        //名称
        private String name;
        //种类
        private GType goodsType;
        //价格
        private Float price;
        //数量
        private Integer count;
    
        public Goods(String name, GType goodsType, Float price, Integer count) {
            this.name = name;
            this.goodsType = goodsType;
            this.price = price;
            this.count = count;
        }
        //setter and getter
	}
	
    //进货
    private static List<Goods> stock() {
        return Arrays.asList(new Goods("apple", GType.FRUITS, 5.0F, 30),
                new Goods("banana", GType.FRUITS, 3.5F, 20),
                new Goods("orange", GType.FRUITS, 8.0F, 40),
                new Goods("bread", GType.FOODS, 3.9F, 15),
                new Goods("milk", GType.DRINK, 6.0F, 100),
                new Goods("grape", GType.FRUITS, null, 10),
                new Goods("beer", GType.DRINK, 8.0F, 100),
                new Goods("cookie", GType.FOODS, 2.6F, 300),
                new Goods("sugar", GType.FOODS, 1.0F, 150),
                new Goods("moutai", GType.DRINK, 2980.0F, 1));
    }

接着要面对各种购物需求

    public static void main(String[] args) {
        List<Goods> goods = stock();

        System.out.println("有哪些商品?------------------");
        //都买哪些商品?
        String goodsNames = goods.stream()
                .map(Goods::getName)
                .collect(Collectors.joining(", "));
        System.out.println(goodsNames);
		//apple, banana, orange, bread, milk, grape, beer, cookie, sugar, moutai

        System.out.println("有几类商品?------------------");
        //有几类商品?
        long types = goods.stream()
                .map(Goods::getGoodsType)
                .count();
        System.out.println(types);
		//10
		
        System.out.println("有哪些水果?------------------");
        System.out.println("名称-种类-价格-数量");
        //有哪些水果?
        goods.stream()
                .filter(e -> e.getGoodsType().equals(GType.FRUITS))
                .forEach(System.out::println);
		//名称-种类-价格-数量
		//apple - 1 - 5.0 - 30
		//banana - 1 - 3.5 - 20
		//orange - 1 - 8.0 - 40
		//grape - 1 - 18.3 - 10

        System.out.println("最贵的商品------------------");
        //最贵的商品
        Goods dearly = goods.stream()
                .max(Comparator.comparingDouble(Goods::getPrice))
                .get();
        System.out.println(dearly.getName());
		//moutai
	
        System.out.println("最便宜的价格------------------");
        //最便宜的价格
        Float cheap = goods.stream()
                .map(Goods::getPrice)
                .sorted(Float::compare)
                .limit(1)
                .findAny()
                .get();
        System.out.println(cheap);
		//1.0

        System.out.println("饮品按名字排序------------------");
        //饮品按名字排序
        List<Goods> sortedDrinks = goods.stream()
                .filter(e -> e.getGoodsType().equals(GType.DRINK))
                .sorted(Comparator.comparing(Goods::getName))
                .collect(Collectors.toList());
        sortedDrinks.forEach(System.out::println);
		//beer - 3 - 8.0 - 100
		//milk - 3 - 6.0 - 100
		//moutai - 3 - 2980.0 - 1

        //食品库存量
        System.out.println("所有商品库存量------------------");
        int goodsCount = goods.stream()
                .mapToInt(Goods::getCount)
                .sum();
        System.out.println(goodsCount);
		//766
		
        //是否有免费的商品
        System.out.println("是否有免费的商品------------------");
        boolean freeGoods = goods.stream()
                .anyMatch(e -> e.getPrice() == 0);
        System.out.println(freeGoods);
		//false
		
        //以商品种类分类
        System.out.println("以商品种类分类------------------");
        Map<GType, List<Goods>> goodsMap = goods.stream()
                .collect(Collectors.groupingBy(Goods::getGoodsType));
        System.out.println(goodsMap);
		//{FOODS=[bread - 2 - 3.9 - 15, cookie - 2 - 2.6 - 300, sugar - 2 - 1.0 - 150], FRUITS=[apple - 1 - 5.0 - 30, banana - 1 - 3.5 - 20, orange - 1 - 8.0 - 40, grape - 1 - 18.3 - 10], DRINK=[milk - 3 - 6.0 - 100, beer - 3 - 8.0 - 100, moutai - 3 - 2980.0 - 1]}
		
        //每个商品的余量
        System.out.println("每个商品的余量------------------");
        Map<String, Integer> goodsCountMap = goods.stream()
                .collect(Collectors.toMap(Goods::getName, Goods::getCount));
        System.out.println(goodsMap);
        //{orange=40, banana=20, apple=30, bread=15, cookie=300, milk=100, moutai=1, grape=10, sugar=150, beer=100}
    }

使用流时要注意一点,如果没有终端操作的话,流是不会执行的哦。

goods.stream().peek(e -> System.out.print(e.getName() + " "));
//
goods.stream().peek(e -> System.out.print(e.getName() + "-")).count();
//apple-banana-orange-bread-milk-grape-beer-cookie-sugar-moutai-
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: Stream和Lambda表达式Java 8引入的重要特性。Stream是Java中用于操作集合的一种新方式,可以方便地对集合进行过滤、映射、排序等操作。而Lambda表达式则是一种简洁的函数式编程的方式,可以将代码作为数据进行传递和处理。 在使用Stream和Lambda表达式时,可以通过filter方法来排除某些元素。例如,可以使用filter((e) -> e.getAge() > 2)来筛选出年龄大于2的员工。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java8新特性详解](https://blog.csdn.net/qq_36144703/article/details/109094750)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java新特性:Stream流和Lambda表达式](https://blog.csdn.net/m0_63411861/article/details/130667884)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java——Lambda&Stream表达式](https://blog.csdn.net/xinin919/article/details/126629924)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值