行为参数化传递
1.首先看一个小的例子
- 我们要将一个库存排序,再进行苹果重量比较
// inventory 库存
Collections.sort(inventory, new Comparator<Apple>() {
public int compare(Apple a1,Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
而用Stream来了做的话,是通过传递代码 将操作行为实现参数化的思想
inventory.sort(comparing(Apple::getWeight));
与1如出一辙的例子
- 想要筛选一个目录中的所有隐藏文件
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isHidden();
}
})
我们已经有了isHidden方法,那么我们将此方法传入函数中
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
- 在Java 8里写下File::isHidden的时候,就创建了一个方法引用,同样可以传递它。
继续看一个例子
苹果类
public static class Apple {
private int weight = 0;
private String color = "";
public Apple(int weight, String color){
this.weight = weight;
this.color = color;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
- 找出重量大于150的苹果
- 找出苹果的颜色是绿色的苹果 不推荐的写法如下
List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
new Apple(155, "green"),
new Apple(120, "red"));
public static List<Apple> filterGreenApples(List<Apple> inventory){ //颜色
List<Apple> res = new ArrayList<>();
for(Apple apple : inventory){
if("green".equals(apple.getColor())){
res.add(apple);
}
}
return res;
}
public static List<Apple> filterHeavyApples(List<Apple> inventory){ //重量
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getWeight() > 150) {
result.add(apple);
}
}
return result;
}
推荐写法
public interface Predicate<T>{
boolean test(T t);
}
public static boolean isGreenApple(Apple apple) {
return "green".equals(apple.getColor());
}
public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
public static void main(String []args){
List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
new Apple(155, "green"),
new Apple(120, "red"));
List<Apple> greenA1 = filterApples(inventory,FilteringApples::isGreenApple);
List<Apple> greenA2 = filterApples(inventory,(Apple a) -> "green".equals(a.getColor()));
System.out.println("------" + greenA1 + "------");
System.out.println("···" + greenA2 + "···");
}
- 运行结果
- Predicate 接口定义了一个名叫 test 的抽象方法,它接受泛型T 对象
- 并返回一个boolean 。这恰恰和你先前创建的一样,现在就可以直接使用了 ==例如可以这样传参==
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
- ※泛型Predicate接口里面的方法是boolean类型,所以可以使用行为参数化传递
Stream小例子
- 从一个列表中筛选金额较高的交易,然后按货币分组
for (Transaction transaction : transactions) {
if(transaction.getPrice() > 1000){
Currency currency = transaction.getCurrency();
List<Transaction> transactionsForCurrency =
transactionsByCurrencies.get(currency);
if (transactionsForCurrency == null) {
transactionsForCurrency = new ArrayList<>();
transactionsByCurrencies.put(currency,
transactionsForCurrency);
}
transactionsForCurrency.add(transaction);
}
}
上面的代码转化为
Map<Currency, List<Transaction>> transactionsByCurrencies =
transactions.stream()
.filter((Transaction t) -> t.getPrice() > 1000)
.collect(groupingBy(Transaction::getCurrency));
Stream的强大
- 有这样一个需求
- 选出400卡路里以下的菜肴,按照卡路里排序
List<String> lowCaloricDishesName =
menu.stream()
.filter(d -> d.getCalories() < 400) //400以下
.sorted(comparing(Dish::getCalories)) //排序
.map(Dish::getName) // 提取菜肴的名称
.collect(toList()); // 保存
- 把几个基础操作链接起来,来表达复杂的数据处理流水线
- 在 filter 后面接上sorted 、 map 和 collect 操作
-
元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。
-
源——流会使用一个提供数据的源,如集合、数组或输入/输出资源。
-
数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作。
-
如 filter 、 map 、 reduce 、 find 、 match 、 sort 等。
-
流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
-
内部迭代——与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。
-
filter——接受Lambda,从流中排除某些元素。
-
map——接受一个Lambda,将元素转换成其他形式或提取信息。
-
limit ——截断流,使其元素不超过给定数量。
-
collect ——将流转换为其他形式。
lambda小特性
public interface Consumer<T>{
void accept(T t);
}
public static <T> void forEach(List<T> list,Consumer<T> c){
for (T i : list){
c.accept(i);
}
}
public static void main(String[] args) {
forEach(
Arrays.asList(1,2,3,4,5),
(Integer i) -> System.out.println(i)
);
}
- Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象
- 没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口
- 创建一个forEach方法,接受一个Integers的列表,并对其中每个元素执行操作
筛选与切片
谓词筛选filter
筛选出所有素菜,创建一张素食菜单
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
筛选各异的元素
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
截短
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
跳过元素
返回一个扔掉了前n个元素的流
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
映射
- map它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
因为getName方法返回一个String,所以map方法输出的流的类型就是Stream。
例2
给定一个单词列表,你想要返回另一个列表,显示每个单词中有几个字母
List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<Integer> wordLen = words.stream()
.map(String::length)
.collect(toList());
##查找元素 找到一道素食菜肴
Optional<Dish> dish =
menu.stream()
.filter(Dish::isVegetarian)
.findAny();
- Optional类(java.util.Optional)是一个容器类,代表一个值存在或不存在
- isPresent() 将在Optional包含值的时候返回true,否则返回false。
- ifPresent(Consumer block) 会在值存在的时候执行给定的代码块。
- T get() 会在值存在时返回值,否则抛出一个 NoSuchElement 异常。
- T orElse(T other) 会在值存在时返回值,否则返回一个默认值。
// 如果找到蔬菜那么就输出它的名字
menu.stream()
.filter(Dish::isVegetarian)
.findAny()
.ifPresent(d -> System.out.println(d.getName());