Java8 Stream API
定义
流是JavaAPI的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集合的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码了
与传统写法的优势
- 声明性——更简洁,更易读
- 可复合——更灵活
- 可并行——性能更好
演示代码:
public class StreamDemo1 {
public static void main(String[] args) {
List<Dish> menu = Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 300, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH) );
//传统的编码方式:需要三次,因为必须筛选出菜,才能排序,然后才能得到符合条件排好序的菜的名称列表
//第一个方法,筛选出卡路里低于400的菜
List<Dish> lowCaloricDishes = new ArrayList<>();
for (Dish d : menu) {
if (d.getCalories() < 400) {
lowCaloricDishes.add(d);
}
}
//第二个方法,根据卡路里排序
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2) {
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
//第三个方法,循环得到其中的菜的名字组成新的集合
List<String> lowCaloricDishesName = new ArrayList<>();
for (Dish d : lowCaloricDishes) {
lowCaloricDishesName.add(d.getName());
}
for(String name: lowCaloricDishesName){
System.out.println("name");
}
System.out.println("---------------------");
//Stream API实现,只需要下指令,比如先过滤排序获取姓名,组成新的数组返回
List<String> streamRes =
menu.stream()
.filter(d -> d.getCalories() < 400)
.sorted(Comparator.comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
streamRes.stream().forEach(System.out::println);
//如果你需要并行处理,只需要在生成流的时候使用并行流parallelStream
List<String> streamRes =
menu.parallelStream()
.filter(d -> d.getCalories() < 400)
.sorted(Comparator.comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
streamRes.stream().forEach(System.out::println);
//season fruit,prawns,rice
}
}
流的特性
- 只能处理一次;请注意,和迭代器类似,流只能迭代一次。处理完之后,我们就说这个流已经被消费掉了流,不能再次使用
- 内部迭代;内部已经做好了优化,方便并行的方式处理,减少了外部迭代时并行处理时面对的问题
流操作
- 中间操作。依然返回一个流,达到流水线的处理方式
- 中断操作。返回的结果不是流,
操作 filter 过滤
map 提取
reduce 归约 组合
find 查询
match 匹配
sort 排序
distinct 判断原则根据元素的hashcode和equals方法
skip 跳过元素
一些中间操作的代码示例:
public class StreamDemo {
public static void main(String[] args) {
List<Dish> menu = Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 300, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH));
//第一个需求:找出卡路里高于300的菜品的名字
List<String> s = menu.stream().filter(d -> d.getCalories() > 300)
.sorted((s1, s2) -> s1.getCalories() - s2.getCalories())
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)// == Function<Dish , String> f2 = (a)->a.getName(); .map(f2)
.limit(3)
.collect(toList());
//allMatch用法,判断是不是都大于100卡路里
boolean a = menu.stream().allMatch(aa -> aa.getCalories() > 100);
System.out.println(a);
//find test
menu.stream().filter(d -> d.getCalories() > 30000)
.findAny()
.ifPresent(d -> System.out.println(d.getName()));
//find first
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst();// 9
if (firstSquareDivisibleByThree.isPresent()) {
int value = firstSquareDivisibleByThree.get();
System.out.println(value);
}
//也可以在流处理中直接处理optional
someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst().ifPresent(System.out::println);// 9
//reduce的使用 计算总卡路里值
//由于没有reduce方法设置默认值,所以为了防止空结果,返回optional
menu.stream().map(Dish::getCalories).reduce((e1, e2) -> e1 + e2).ifPresent(System.out::println);
//利用方法引用,Integer类现在有了一个静态的sum方法来对两个数求和
menu.stream().map(Dish::getCalories).reduce(Integer::sum).ifPresent(System.out::println);
//rudecu方法传入默认值,则返回结果而不是optional
int sum = menu.stream().map(Dish::getCalories).reduce(100,Integer::sum);
//先用map构建数值流,在求和 这种方式避免了Integer::sum的装箱操作
int total = menu.stream().mapToInt(Dish::getCalories).sum();
//求最大值max/最小值min方法
int total2 = menu.stream().mapToInt(Dish::getCalories).max().orElse(1);
menu.stream().map(Dish::getCalories).reduce(Integer::max).ifPresent(System.out::println);
menu.stream().map(Dish::getCalories).reduce(Integer::min).ifPresent(System.out::println);
}
}