1.Java Streams API 是 Java 8 中引入的一个重要特性。这个特性在 Java 8 的发布中正式出现,发布于 2014 年 3 月 18 日。Streams API 旨在提供一种更简洁和功能强大的方式来处理集合数据的操作,如过滤、映射和归约等。
2.主要设计目标
- 简洁性:提供一种更简洁、声明性的方式来处理数据集合,避免了传统的迭代代码的复杂性。
- 并行处理:支持并行流(parallel streams),使得开发者可以更轻松地利用多核处理器来提高性能。
- 函数式编程:引入了函数式编程的概念,例如 lambda 表达式和方法引用,使得对数据的操作更加直观。
常用用法 :
两个代码,里面常用的方法几乎都有示例,写得不对的地方,欢迎大佬纠正
实体类
package cn.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.*;
import java.math.BigDecimal;
/**
* @author huang
*/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Comparable<Product> {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private BigDecimal price;
@Override
public int compareTo(Product product) {
return this.price.compareTo( product.price);
}
public Product(Long id, BigDecimal price, String name) {
this.id = id;
this.price = price;
this.name = name;
}
}
测试代码和用法
package cn.xiaoyunyun.test;
import cn.xiaoyunyun.domain.Product;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Lenovo
*/
public class SteamDemo {
public static void main(String[] args) {
Product product = new Product();
product.setId(Long.parseLong("123"));
product.setPrice(new BigDecimal("1.5"));
product.setName("冰棒");
Product coke = new Product();
coke.setId(Long.parseLong("456"));
coke.setPrice(new BigDecimal("3.5"));
coke.setName("可乐");
Product water = new Product();
water.setId(Long.parseLong("6789"));
water.setPrice(new BigDecimal("2.5"));
water.setName("矿泉水");
Product masterK = new Product();
masterK.setId(Long.parseLong("888"));
masterK.setPrice(new BigDecimal("3"));
masterK.setName("康师傅");
Product iceTea = new Product();
iceTea.setId(Long.parseLong("1122"));
iceTea.setPrice(new BigDecimal("4.0"));
iceTea.setName("冰红茶");
Product redBull = new Product();
redBull.setId(Long.parseLong("3344"));
redBull.setPrice(new BigDecimal("5.0"));
redBull.setName("红牛");
Product eastPong = new Product();
eastPong.setId(Long.parseLong("5566"));
eastPong.setPrice(new BigDecimal("2.8"));
eastPong.setName("东鹏特饮");
Product sprite = new Product();
sprite.setId(Long.parseLong("7788"));
sprite.setPrice(new BigDecimal("3.2"));
sprite.setName("雪碧");
Product fanta = new Product();
fanta.setId(Long.parseLong("9900"));
fanta.setPrice(new BigDecimal("3.6"));
fanta.setName("芬达");
ArrayList<Product> list = new ArrayList<>();
ArrayList<Product> listTwo = new ArrayList<>();
list.add(product);
list.add(coke);
list.add(water);
list.add(masterK);
list.add(iceTea);
list.add(redBull);
list.add(eastPong);
list.add(sprite);
list.add(coke);
list.add(fanta);
listTwo.add(water);
//转换成名字list
List<String> nameList = list.stream().map(Product::getName).collect(Collectors.toList());
// 过滤出价格大于 1.5 的产品
List<BigDecimal> priceFilter = list.stream()
.map(Product::getPrice)
.filter(price -> price.compareTo(new BigDecimal("1.5")) > 0)
.collect(Collectors.toList());
priceFilter.forEach(prices -> System.out.println("product1 = " + prices));
// 任意一个产品的名字与第一个产品的名字匹配则返回 true
boolean any = list.stream().anyMatch(x -> x.getName().equals(list.get(0).getName()));
System.out.println("anyMatch->任意有一个条件匹配则为" + any);
// 所有产品的名字是否都与 listTwo 中第一个产品的名字相同
boolean all = list.stream().allMatch(x -> x.getName().equals(listTwo.get(0).getName()));
System.out.println("allMatch->任意有一个条件匹配则为" + all);
// 任意一个产品的名字与 listTwo 中第一个产品的名字不匹配则返回 true
boolean none = list.stream().noneMatch(x -> x.getName().equals(listTwo.get(0).getName()));
System.out.println("noneMatch->任意有一个不匹配则为" + none);
//返回集合中的第一个实例
Optional<Product> first = list.stream().findFirst();
System.out.println("findFirst返回第一个实例" + first);
//这些方法在处理可能为空的值时非常有用,可以帮助避免 NullPointerException 并提供清晰的默认值处理机制。
// 查找价格大于 8 的第一个产品的价格,若没有找到,则使用默认值
Product deProduct = new Product();
BigDecimal defaultPrice = new BigDecimal("0.00");
deProduct.setPrice(defaultPrice);
Product deProductNew = list.stream().
filter(price -> price.getPrice().compareTo(new BigDecimal("8")) > 0).
findFirst().orElse(deProduct);
System.out.println("deProductNew = " + deProductNew);
//当 Optional 为空时通过 Supplier 函数生成一个默认值。
Product orElseGet = list.stream().
filter(price -> price.getPrice().compareTo(new BigDecimal("8")) > 0).
findFirst().orElseGet(() -> {
Product defaultProduct = new Product();
defaultProduct.setId(Long.parseLong("0"));
defaultProduct.setName("默认");
defaultProduct.setPrice(new BigDecimal("0.00"));
return defaultProduct;
});
System.out.println("orElseGet = " + orElseGet);
//去掉重复的数据
List<Product> distinct = list.stream().distinct().collect(Collectors.toList());
System.out.println("distinct = " + distinct);
//判断数据是否重复
long count = list.stream().map(Product::getName).distinct().count();
if (count != list.size()) {
System.out.println("存在重复数据,请检查数据!");
}
//模拟百度网盘操作
//需要更改的list
List<Product> updateList = list.stream().filter(x -> x.getId() != null).map(x -> {
Product po = new Product();
po.setId(x.getId());
po.setName(x.getName());
return po;
}).collect(Collectors.toList());
//需要删除的list
// 假设有一个数据库的数据
ArrayList<Product> mysqlList = new ArrayList<>();
List<Long> deleteList = mysqlList.stream().map(Product::getId).filter(x -> updateList.stream().noneMatch(y -> x.equals(y.getId()))).collect(Collectors.toList());
// 新增的数据
List<Product> insertList = list.stream().filter(x -> x.getId() == null).map(x -> {
Product po = new Product();
po.setId(x.getId());
po.setName(x.getName());
return po;
}).collect(Collectors.toList());
//先删除 再新增 再修改 baseMapper.deleteByIds(deleteList);
list.stream().sorted()
.forEach(System.out::println);
//如果在类中没实现Comparable 会报Exception in thread "main" java.lang.ClassCastException: product类放在后面了
//如果按照价格降序的写法
list.stream().sorted(Comparator.comparing(Product::getPrice).reversed())
.forEach(System.out::print);
//返回一个 Optional<Product>,该 Optional 包含流中价格最大的 Product 对象。如果流为空,则返回 Optional.empty()
Optional<Product> maxProduct = list.stream().max(Comparator.comparing(Product::getPrice));
System.out.println("用了max会返回该集合中最大的一个价格最大的maxProduct = " + maxProduct);
//结合 ifPresent一起使用 如果 maxPriceProduct 有值(即流不为空),则打印出价格最高的产品。
maxProduct.ifPresent(productIP -> System.out.println("不为null的时候返回价格最高的商品" + productIP));
//返回一个 Optional<Product>,该 Optional 包含流中价格最小的 Product 对象。如果流为空,则返回 Optional.empty() 测试list为null的情况 list.removeAll(list);
Optional<Product> minProduct = list.stream().min(Comparator.comparing(Product::getPrice));
System.out.println("用了min会返回该集合中最大的一个价格最小的minProduct = " + minProduct);
//用于从流中找到任意一个元素。它返回一个 Optional 对象,这个对象可能包含流中的一个元素,也可能为空(如果流为空)。如果是顺序流的的话,会返回第一个,用并行流的话会返回随机一个
Optional<Product> anyOneProduct = list.stream().findAny();
System.out.println("用了findAny会返回该集合中任意一个元素 = " + anyOneProduct);
Optional<Product> anyParallelProduct = list.parallelStream().findAny();
System.out.println("用了parallelStream 中的findAny会返回该集合中任意一个元素 = " + anyParallelProduct);
//验证parallelStream
int j = 100;
for (int i = 0; i < j; i++) {
Optional<Product> any1 = list.parallelStream().findAny();
System.out.println("any1 = " + any1 + i);
}
// flatMap 方法接受一个函数,这个函数将流中的每个元素转换成一个新的流(或其他可迭代对象)。然后,flatMap 将这些流合并成一个单一的
// 示例 1 提取所有产品的价格并合并成一个流
List<BigDecimal> flatList = list.stream().
flatMap(productFlat -> Arrays.stream(new BigDecimal[]{productFlat.getPrice()})).
collect(Collectors.toList());
System.out.println("flatList = " + flatList);
// 处理 List<Product> 列表的嵌套列表
// 假设这些是 Product 类的对象,已经按题目代码创建好
List<Product> list1 = Arrays.asList(
new Product(123L, new BigDecimal("1.5"), "冰棒"),
new Product(456L, new BigDecimal("3.5"), "可乐")
);
List<Product> list2 = Arrays.asList(
new Product(6789L, new BigDecimal("2.5"), "矿泉水"),
new Product(888L, new BigDecimal("3.0"), "康师傅")
);
List<List<Product>> nestedList = Arrays.asList(list1, list2);
List<Product> allProducts = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
//如果直接list1.addAll(list2); UnsupportedOperationException,因为 Arrays.asList 返回的列表是固定大小的。你不能修改这个列表,比如添加或删除元素。
//另外一种写法
ArrayList<Object> list3 = new ArrayList<>();
list3.addAll(list1);
list3.addAll(list2);
System.out.println(allProducts);
System.out.println(list3);
// 使用 mapToInt 计算所有产品价格乘以 2 后的总和 乘以 2 并转换为 int
int total = list.stream()
.mapToInt(p -> p.getPrice().multiply(BigDecimal.valueOf(2)).intValue())
.sum();
// 精度计算不够
System.out.println("Total price after doubling: " + total);
// 计算正确 价格x2并累加
BigDecimal reduce = list.stream().map(Product::getPrice).
map(n -> n.multiply(BigDecimal.valueOf(2)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("过滤出价格并且乘以2,然后一直累加结果 = " + reduce);
//flatMapToDouble, flatMapToInt, 和 flatMapToLong 是 Java Stream API 中的三个方法,用于将流中的元素映射为原始类型流。这些方法常用于处理原始数据类型,提供高效的操作方式。
// skip 跳过第几个 limit 限制输出的元素为 2 个
list.stream().skip(3)
.limit(2).forEach(System.out::println);
System.out.println("forEachOrdered >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//没有并行流的情况下这两个是等价的
list.stream().forEachOrdered(System.out::println);
list.forEach(System.out::println);
//forEachOrdered 常用于需要保持特定顺序输出的业务场景,特别是在处理并行流时。
List<Integer> listNumber = Arrays.asList(1, 4, 3, 2, 5);
listNumber.parallelStream().forEachOrdered(System.out::println);
//peek() 是 Java Stream API 中的一个中间操作,用于对流中的每个元素进行查看和处理,但不会改变流的元素。它通常用于调试或查看流中的元素。
List<Product> collect = list.stream().
peek(peek -> System.out.println("peek = " + peek.getName())).
collect(Collectors.toList());
//iterator() 方法用于从流中获取一个迭代器。这个迭代器可以用来遍历流中的元素
list.stream().iterator();
// 在 Java 中,迭代器(Iterator)和增强型 for 循环(也称为“for-each”循环)是遍历集合和数组的两种常用方法。它们各有优缺点和适用场景。
List<Product> removedProducts = new ArrayList<>();
Iterator<Product> iterator = list.iterator();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
while (iterator.hasNext()) {
Product product3 = iterator.next();
if (product3.getPrice().compareTo(new BigDecimal("3.5")) == 0) {
removedProducts.add(product3);
iterator.remove();
}
}
System.out.println("Removed products: " + removedProducts);
System.out.println("删除了价格为3.5的产品: " + list);
//list.stream().dropWhile() 是 Java Stream API 中的方法,用于在流中跳过满足指定条件的元素,直到遇到第一个不满足条件的元素为止。之后,流中的所有剩余元素都会被保留。
list.stream().dropWhile(product1 -> product1.getName().
equals("可乐")).
forEach(System.out::println);
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 3, 6, 7, 8, 9);
// 使用 dropWhile 方法 跳过所有小于 5 的元素
numbers.stream()
.dropWhile(n -> n < 5)
.forEach(System.out::println);
List<String> stringList = list.stream().map(Product::getName).
dropWhile(product1 -> !product1.equals("可乐")).
collect(Collectors.toList());
System.out.println(" = >>>>>>>>>>>>>>>>>>>>>>>>>>>" + stringList);
// 使用 takeWhile 方法
List<Integer> takeWhileNumbers = Arrays.asList(1, 2, 3, 4, 5, 7, 8, 9);
List<Integer> result = takeWhileNumbers.stream()
.takeWhile(n -> n < 5)
.collect(Collectors.toList());
// 输出: [1, 2, 3, 4]
System.out.println(result);
// 使用 sequential 方法 是一个用于将流的处理方式从并行模式转换回顺序模式的方法。虽然 sequential() 不是直接链式操作的常见方法,但它的作用是在流链中将流重新设置为顺序流。
List<Integer> demoNumbers = Arrays.asList(1, 2, 3, 4, 5);
// 创建并行流 将并行流转换回顺序流 映射操作 收集结果
List<Integer> sequentialResult = demoNumbers.parallelStream()
.sequential()
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println(sequentialResult);
// spliterator() 是 Stream 接口中的一个方法,用于返回一个 Spliterator 对象。Spliterator 是一个新的接口,提供了一种遍历和分割数据源的方式,支持并行操作。
list.stream().spliterator();
// 这两个找了百度说是无效的 看了源码也不是java实现的
list.stream().notify();
list.stream().notifyAll();
}
}
如果工作中碰到其他的我再补充~