Lambda 表达式与 Stream API

初创建于: 2022-07-27 07:48

Lambda 表达式

Lambda 表达式允许把一个函数作为一个方法的参数, lambda 表达式的语法格式如下:

{ params } -> expression
// 或
{ params } -> { statements; }

这里给出一个简单的例子:

package test;

public class Main {
	public static void main(String[] args) {
		Operation x = () -> 5;
		test(x);
	}

	static void test(Operation f) {
		System.out.println(f.execute());
	}

	interface Operation {
		int execute();
	}
}
/**
 * javac -d . Main.java
 * java test.Main
 * output: 5
 */

又如以下程序片段

@Override
public Map<String, Object> getBlogListOfConcernedUser(PageParam pageParam, Integer userId) {
	class queryAction implements QueryAction<BlogVO> {
		@Override
		public List<BlogVO> execute() {
			List<BlogVO> blogVOList = blogHomeDao.getArticleListOfConcernedUser(userId);
			return blogVOList;
		}
	}

	queryAction query = new queryAction();
	return Utils.getPage(pageParam, query);
}

使用 lambda 表达式重写后, 可以简化为:

@Override
public Map<String, Object> getBlogListOfConcernedUser(PageParam pageParam, Integer userId) {
	return Utils.getPage(pageParam, () -> blogHomeDao.getArticleListOfConcernedUser(userId));
}

在 lambda 表达式中不能修改变量的值.


Stream API

Stream API 详解

Stream 的创建

  1. 通过集合生成;
    List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
    Stream<Integer> stream = integerList.stream();
    
  2. 通过数组生成;
    int[] intArr = new int[]{1, 2, 3, 4, 5};
    IntStream stream = Arrays.stream(intArr);
    
  3. 通过静态方法生成;
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
    
  4. 通过文件生成;
    Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())
    
  5. 通过函数生成.
    Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
    

流的操作

流的操作主要可以分为两种: 中间操作终端操作

中间操作

一个流可以后面跟随零个或多个中间操作. 其目的主要是打开流, 做出某种程度的数据映射/过滤, 然后返回一个新的流, 交给下一个操作使用. 这类操作都是惰性化的, 仅仅调用到这类方法, 并没有真正开始流的遍历, 真正的遍历需等到终端操作时, 常见的中间操作有下面即将介绍的filter、map等.

filter 筛选

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = intList.stream().filter(n -> n > 3);

distinct 去重

Stream<Integer> stream = intList.stream().distinct();

limit 返回指定个数的元素

// 只返回前 3 个元素
Stream<Integer> stream = intList.stream().limit(3);

skip 跳过指定个数元素

// 跳过前两个元素
Stream<Integer> stream = intList.stream().skip(2);

map 流映射

将接收的元素转换为另一个元素.

List<String> stringList = Arrays.asList("Corona", "Hello", "World", "Hello World");
List<Integer> lengths = stringList.stream().map(String::length).collect(Collectors.toList());

元素匹配

匹配的结果是一个 boolean 值, 匹配成功返回 true; 匹配失败返回 false.

  1. allMatch 匹配所有

    List<String> stringList = Arrays.asList("Corona", "Hello", "World", "Hello World");
    Boolean ok1 = stringList.stream().allMatch(s -> s.length() > 3);
    

    如上, 当 stringList 中的所有字符串长度都 > 3 时才会返回 true.

  2. anyMatch 匹配一个

    List<String> stringList = Arrays.asList("Corona", "Hello", "World", "Hello World");
    Boolean ok2 = stringList.stream().anyMatch(s -> s.length() == 11);
    

    如上, 当 stringList 中的有一个字符串长度为 11, 即返回 true.

  3. noneMatch 全不匹配

    List<String> stringList = Arrays.asList("Corona", "Hello", "World", "Hello World");
    Boolean ok3 = stringList.stream().noneMatch(s -> s.length() < 3);
    

    如上, 当 stringList 中没有字符串长度 < 1 时返回 true.

终端操作

一个流有且只能有一个终端操作, 当这个操作执行后, 流就被关闭了, 无法再被操作. 因此一个流只能被遍历一次, 若想在遍历需要通过源数据在生成流. 终端操作的执行, 才会真正开始流的遍历. 如下面即将介绍的count、collect等.

例子

假设现在有如下要求: 将从数据库中查询到的数据 (菜的列表) 进行处理:

  1. 筛选出卡路里 < 400 的菜;
  2. 对筛选后的菜按卡路里值升序排序;
  3. 获取排序后的菜的名字.
public class Dish {
	private String name;
	private Integer calories;
}

正常情况下, 会有如下流程:

package test;
import test.dish.Dish;
import java.util.*;

public class Main {
	public static List<Dish> dishList = new ArrayList<>();
	static {
		String[] dishNameList = new String[]{"Apple", "Banana", "PineApple", "Hello World", "Bean"};
		Integer[] dishCaloryList = new Integer[]{500, 300, 200, 100, 400};

		for (int i = 0; i < 5; i++) {
			dishList.add(new Dish(dishNameList[i], dishCaloryList[i]));
		}
	}

	public static void main(String[] args) {
		List<Dish> lowCaloricDishes = new ArrayList<>();
		// 1. 筛选出卡路里 < 400 的菜
		for (Dish dish : dishList) {
			if (dish.getCalories() < 400) {
				lowCaloricDishes.add(dish);
			}
		}

		// 2. 对筛选出来的菜进行排序
		Collections.sort(lowCaloricDishes, new Comparator<Dish>() {            
			@Override            
			public int compare(Dish o1, Dish o2) {                
				return Integer.compare(o1.getCalories(), o2.getCalories());           
			}        
		});

		// 3. 获取菜的名字
		List<String> nameList = new ArrayList<>();
		for (Dish dish : lowCaloricDishes) {
			nameList.add(dish.getName());
		}

		for (int i = 0; i < nameList.size(); i++) {
			System.out.println(nameList.get(i));
		}
	}
}
posted @ 2023-01-09 23:25  昤昽  阅读(20)  评论(0编辑  收藏  举报