Java流及流操作示例

简介: Java流及流操作示例


此处的流(Stream)与io中的输入流(InputStream)与输出流(OutputStream)是不同的概念,与实时处理数据的流也是不同的概念,但它们也有相似之处。


Stream是对集合类的增强,它将List、Map等集合作为数据源,串行或并行地操作集合类,这些操作包括遍历(foreach)、过滤(filter)、排序(sort)、匹配(match)、映射(map)、计数(count)等,上述操作分为两种类型:


1、中间操作:该操作将会计算数据,返回计算后的流


2、终端操作:该操作会关闭流,返回最终数据


为了进行运算,Stream操作被放入到流管道中,流管道由数据源(可以是数组、集合、I/O通道等)、转换流的中间操作(比如filter)、返回数据结果的最终操作组成(比如count和foreach)。


流管道示意图:



image.png

Java中流特性:


  • 不是数据结构
  • 并不储存数据,所有的操作从源抓取
  • 不会影响源数据
  • 不支持索引访问(但可以使用IntStream曲线救国)
  • 惰性
  • 只有最终操作被启动时,才会计算源数据
  • 只有当需要的时候才使用源元素(提供源数据的元素)
  • 并行


BaseStream


所有的流都继承此接口


java
// 继承自AutoCloseable,说明流都是自动关闭的
public interface BaseStream<T, S extends BaseStream<T, S>>
        extends AutoCloseable {
    // 下面是所有流都拥有的方法
    // 返回当前流元素的迭代器(终端操作)
    Iterator<T> iterator();
    // 返回当前流元素的可切割迭代器(终端操作)
    Spliterator<T> spliterator();
    // 是否并行流
    boolean isParallel();
    // 返回当前流的串行流,若当前流已经是串行或流已经被修改为串行,返回自己
    S sequential();
    // 返回当前流的并行流,若当前流已经是并行或流已经被修改为并行,返回自己
    S parallel();
    // 返回当前流的乱序流,若当前流已经是乱序或流已经被修改为乱序,返回自己
    S unordered();
    // 返回添加了额外的流关闭处理器的流,当close()方法执行的时候调用。
    // 所有的流关闭处理器都会被执行,即使某处理器发生异常,
    S onClose(Runnable closeHandler);
    void close();
}


Java基于BaseStream提供了众多数据流的封装,其中基于原始数据类型的数据流封装较为常用,比如:IntStream、DoubleStream、LongStream、


Stream


继承自BaseStream      


Stream API如下图:  


微信图片_20220213180014.jpg


Java中流特性:


  • 不是数据结构
  • 并不储存数据,所有的操作从源抓取
  • 不会影响源数据
  • 不支持索引访问(但可以使用IntStream曲线救国)
  • 惰性
  • 只有最终操作被启动时,才会计算源数据
  • 只有当需要的时候才使用源元素(提供源数据的元素)
  • 并行


生成流的几种方式


1、数组方式

java
// 1.
Stream aryStream = Stream.of(ary);
// 2.
Arrays.stream(ary);


2、集合方式

java
// 串行
List<String> list = new ArrayList<>();
list.add("1");
list.add("1");
list.add("1");
Stream stream = list.stream();
// 并行
List<String> list2 = new ArrayList<>();
list2.add("1");
list2.add("1");
list2.add("1");
Stream stream = list.parallelStream();


3、静态工厂

java
// 这种方式可以解决下标问题
int[] ary = {1,2,3,4,5,6,7,8,9};
IntStream.range(0,ary.length).forEach(System.out::println);//   不包含10
IntStream.rangeClosed(0,ary.length).forEach(System.out::println);// 包含10


4、I/O

java
BufferedReader bufferedReader = new BufferedReader(new FileReader("project.properties"));
Stream<String> stream = bufferedReader.lines();


5、其他

java
// 1.
Random random = new Random();
IntStream stream1 = random.ints();
// 2.
Path path = Paths.get("D:\\记事本.txt");
Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);


常用操作示例


下文示例中使用到的Widget实体及集合


java
class Widget{
    private String color;
    private int weight;
    // ...省略g&t
}
private Widget w1 = new Widget("RED",20);
private Widget w2 = new Widget("GREEN",20);
private Widget w3 = new Widget("GRAY",20);
private List<Widget> widgetList = new ArrayList<>();
{
    widgetList.add(w1);
    widgetList.add(w2);
    widgetList.add(w3);
}


filter


java
@Test
public void stream(){
    // 统计红色小组件个数
    System.out.println(widgetList.stream().filter(w -> w.getColor().equals("RED")).count());
    // 统计红色小组件总重
    System.out.println(
            widgetList.stream().filter(w -> w.getColor().equals("RED"))
            //  w -> w.getWeight()
            //  method reference 使用 Widget::getWeight 替代 w -> w.getWeight
            //  方法引用可以用来替换λ表达式
            .mapToInt(Widget::getWeight)
            .sum()
    );
}


filter的入参是谓词 Predicate ,如果一个操作使用频繁,我们可以先写好一个 Predicate ,之后使用它就可以了


java
/*谓词,filter方法接收的编程式函数(functional interface)*/
private static Predicate<Widget> isRedWidget(){
    return widget -> widget.getColor().equals("RED");
}
/*filter forEach forEachRemaining*/
@Test
public void stream(){
    //  predicate
    List<Widget> newWidgets = widgetList.stream().filter(isRedWidget()).collect(Collectors.toList());
    newWidgets.forEach(System.out::println);
    newWidgets.iterator().forEachRemaining(System.out::println);
}
//  输出
com.baosight.test.StreamTest$Widget@26f67b76
com.baosight.test.StreamTest$Widget@26f67b76


map


java
@Test
public void map(){
    String[] ary = {"Hello","world","John sena"};
    // peek不是最终操作,所以下面这条语句不会输出任何东西
    // 这就验证了前面提到的Streams are lazy
    Arrays.stream(ary).map(String::toUpperCase).peek(word -> System.out.println("Mapped value:"+word));
    Arrays.stream(ary).map(String::toUpperCase).peek(word -> System.out.println("Mapped value:"+word)).count();
}
//  输出
Mapped value:HELLO
Mapped value:WORLD
Mapped value:JOHN SENA


collect


java
@Test
public void stream(){
    List<Integer> list = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).collect(Collectors.toList());
    System.out.println(list.toString());
    String string = Stream.of("1","2").collect(Collectors.joining(","));
    System.out.println(string);
    Map widgetMap = widgetList.stream().collect(Collectors.groupingBy(Widget::getColor));
    //  收集行为可以级联
    Map widgetMap2 = widgetList.stream().collect(Collectors.groupingBy(Widget::getColor,Collectors.groupingBy(Widget::getWeight)));
    System.out.println(widgetMap.toString());
}
//  输出
[1, 2, 3, 45, 6, 7, 8, 5, 435]
1,2
{RED=[com.baosight.test.StreamTest$Widget@61a485d2], GRAY=[com.baosight.test.StreamTest$Widget@39fb3ab6], GREEN=[com.baosight.test.StreamTest$Widget@6276ae34]}


reduce


java
/*Instant LongStream.rangeClosed*/
@Test
public void stream(){
    //  instant类用于获取时间线上的时间点
    Instant start = Instant.now();
    // 计算范围内的和
    long r = LongStream.rangeClosed(0, 100000)
            .parallel()// 并行流
            // .sequential() 串行流
            .reduce(0, Long::sum);
    Instant end = Instant.now();
    //  迭代器 forEach和forEachRemaining 的区别:
    //  forEach可以多次调用,对元素多次处理
    //  forEachRemaining对所有元素只处理一次
    //  所以在第二次调用同一个迭代器的forEachRemaining方法时无作为
    Iterator iterator = newWidgets.iterator();
    iterator.forEachRemaining(System.out::println);
    iterator.forEachRemaining(System.out::println);
}
// 输出
5000050000
本次执行耗时:62


sort


java
/*sorted*/
@Test
public void stream(){
    //  排序
    Integer i[] = {1,2,3,5,6,7,9,45,9,324,22};
    List<Integer> integers = Arrays.asList(i);
    integers.stream().sorted().forEach(System.out::println);
}
//  输出
1
2
3
5
6
7
9
9
22
45
324


distinct


java
/*distinct*/
@Test
public void stream(){
    //  去重
    Integer i[] = {1,2,2,2,27,2,2,9,324,22};
    List<Integer> integers = Arrays.asList(i);
    integers.stream().distinct().forEach(System.out::println);
}
//  输出
1
2
27
9
324
22


下标


java
@Test
/*IntStream.range/rangeClosed*/
public void stream(){
    int[] ary = {1,2,3,4,5,6,7,8,9};
//    Stream aryStream = Stream.of(ary);
//    Arrays.stream(ary);
    //IntStream 可以用来当作遍历的下标
    IntStream.range(0,ary.length).forEach(System.out::println);//   不包含10
    IntStream.rangeClosed(0,ary.length).forEach(System.out::println);// 包含10
    IntStream.range(0,ary.length).forEach( i -> System.out.println(i*666));
}


flatMap


java
class Order{
    private String orderNo;
    private String orderName;
    private List<OrderLineItem> lineItems;
    //...省略g&t
}    
class OrderLineItem{
    private String lineNo;
    private String lineName;
    //...省略g&t
}
@Test
/*produce a new stream*/
public void stream(){
    List<Order> orders = new ArrayList<>();
    List<OrderLineItem> lineItems = new ArrayList<>();
    lineItems.add(new OrderLineItem("1", "one"));
    lineItems.add(new OrderLineItem("2", "two"));
    lineItems.add(new OrderLineItem("1", "three"));
    lineItems.add(new OrderLineItem("1", "four"));
    lineItems.add(new OrderLineItem("1", "five"));
    orders.add(new Order("1","one",lineItems));
    orders.add(new Order("2", "two", lineItems));
    orders.add(new Order("3", "three", lineItems));
    // orders的流中将所有orders的lineItems组装起来返回一个新的流,
    // 这个流中有所有order的所有orderLineItem(在当前测试方法中有15个,3*5)
    List data = orders.stream().flatMap(order -> order.getLineItems().stream()).collect(Collectors.toList());
    System.out.println(data.size());
}
//  输出
15


peek


java
/*peek*/
@Test
public void stream(){
    //  就像名字一样,窥视 233
    List<String> list = Stream.of("one","two","three","four","five")
            .filter(e -> e.length() > 3)
            .peek(e -> System.out.println("Filtered value:"+e))
            .map(String::toUpperCase)
            .peek(e -> System.out.println("Maped value:"+e))
            .collect(Collectors.toList());
}
//  输出
Filtered value:three
Maped value:THREE
Filtered value:four
Maped value:FOUR
Filtered value:five
Maped value:FIVE


anyMatch/allMatch/noneMatch


java
@Test
public void match() {
    //  匹配
    boolean match = widgetList.stream().anyMatch(isRedWidget());
    boolean match2 = widgetList.stream().allMatch(isRedWidget());
    boolean match3 = widgetList.stream().noneMatch(isRedWidget());
    System.out.println(match);
    System.out.println(match2);
    System.out.println(match3);
}
//  输出
true
false
false


limit、skip


java
/*limit skip*/
@Test
public void stream(){
    //  两个作用类似,一个限制n个元素,一个跳过n个元素
    List<Integer> list = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).limit(5).collect(Collectors.toList());
    System.out.println(list.toString());
    List<Integer> list2 = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).skip(5).collect(Collectors.toList());
    System.out.println(list.toString());
}
//  输出
[1, 2, 3, 45, 6]
[1, 2, 3, 45, 6]



min、max

java
@Test
public void minAndMax() {
    Comparator comparator = (o1, o2) -> (int) o1 > (int) o2 ? 1 : -1;
    Optional optional = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).min(comparator);
    System.out.println(optional.get());
    Optional optional2 = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).max(comparator);
    System.out.println(optional2.get());
}
// 输出
1
435


findFirst/findAny

java
@Test
public void find() {
    //  findFirst返回流中第一个元素
    //  findAny返回流中任意元素
    Optional optional = widgetList.stream().findFirst();
    Optional optional2 = widgetList.stream().findAny();
    Widget widget = (Widget) optional.get();
    Widget widget2 = (Widget) optional2.get();
    System.out.println(widget.getColor());
    System.out.println(widget2.getColor());
}


toArray

java
/*toArray*/
@Test
public void steam(){
    Widget[] widgets = widgetList.stream().filter(widget -> widget.getColor().equals("RED")).toArray(Widget[]::new);
}
目录
相关文章
|
1月前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
64 1
|
2月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
50 1
|
4月前
|
存储 Java API
【Azure 存储服务】Java Storage SDK 调用 uploadWithResponse 代码示例(询问ChatGTP得代码原型后人力验证)
【Azure 存储服务】Java Storage SDK 调用 uploadWithResponse 代码示例(询问ChatGTP得代码原型后人力验证)
|
1月前
|
Java
在Java中实现接口的具体代码示例
可以根据具体的需求,创建更多的类来实现这个接口,以满足不同形状的计算需求。希望这个示例对你理解在 Java 中如何实现接口有所帮助。
42 1
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
91 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
2月前
|
存储 Java
什么是带有示例的 Java 中的交错数组?
什么是带有示例的 Java 中的交错数组?
56 9
|
2月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
31 1
|
2月前
|
Java API 网络安全
Java 发送邮件示例
本示例展示了如何使用Java编程语言发送电子邮件。通过利用JavaMail API,这段代码实现了从配置SMTP服务器,设置邮件属性,到发送邮件的全过程,为开发者提供了实用的参考。
171 10
|
3月前
|
JavaScript 前端开发 Java
Java 8 新特性详解及应用示例
Java 8 新特性详解及应用示例
|
4月前
|
设计模式 存储 Java
掌握Java设计模式的23种武器(全):深入解析与实战示例
掌握Java设计模式的23种武器(全):深入解析与实战示例