java-流式API:Stream API

Java8引入了一个全新的流式API:Stream API,它位于java.util.stream包中。
这个Stream不同于java.ioInputStreamOutputStream,它代表的是任意Java对象的序列

Stream和List也不一样,List存储的每个元素都是已经存储在内存中的某个Java对象,而Stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的。

Stream可以表示无限序列,元素有可能已经全部存储在内存中,也有可能是根据需要实时计算出来的。一个Stream可以轻易地转换为另一个Stream,而不是修改原Stream本身。真正的计算通常发生在最后结果的获取,也就是惰性计算。

1.创建Stream

最简单的方式是直接用Stream.of()

public class StreamDemo {
    public static void main(String[] args) {
        Stream<Integer> integerStream=Stream.of(1,2,3,4,5);
        integerStream.forEach(System.out::print);
    }
}

再就是基于一个数组或者Collection

public class StreamDemo {
    public static void main(String[] args) {
        Integer[] array = {1, 2, 3, 4, 5};
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(i + 1);
        }

        Stream<Integer> stream1 = Arrays.stream(array);
        Stream<Integer> stream2 = list.stream();

        stream1.forEach(System.out::print);
        stream2.forEach(System.out::print);

    }
}

还可以通过Stream.generate()方法,它需要传入一个Supplier对象

public class StreamDemo {
    public static void main(String[] args) {
        //基于Supplier创建的Stream会不断调用Supplier.get()方法来不断产生下一个元素,
        // 这种Stream保存的不是元素,而是算法,它可以用来表示无限序列。
        Stream<Integer> integerStream = Stream.generate(new Supplier<Integer>() {
            int n = 1;

            @Override
            public Integer get() {
                return n++;
            }
        });
        //无限序列必须先变成有限序列
        integerStream.limit(20).forEach(System.out::println);
    }
}

上述代码我们用一个Supplier<Integer>模拟了一个无限序列(当然受int范围限制不是真的无限大)。如果用List表示,即便在int范围内,也会占用巨大的内存,而Stream几乎不占用空间,因为每个元素都是实时计算出来的,用的时候再算

另外,Files类的lines()方法可以把一个文件变成一个Stream,每个元素代表文件的一行内容:

public class StreamDemo {
    public static void main(String[] args) throws IOException {
        Stream<String> lines = Files.lines(Paths.get("D:\\test.txt"));
        lines.forEach(System.out::println);
    }
}

此方法对于按行遍历文本文件十分有用。


因为Java的泛型不支持基本类型,所以我们无法用Stream<int>这样的类型。为了保存int,只能使用Stream<Integer>,但这样会产生频繁的装箱、拆箱操作,会降低代码执行效率。为了提高效率,Java标准库提供了IntStreamLongStreamDoubleStream这三种使用基本类型的Stream,例如:

public class StreamDemo {
    public static void main(String[] args) throws IOException {
        IntStream intStream=IntStream.generate(new IntSupplier(){
            int n=1;
            @Override
            public int getAsInt() {
                return n++;
            }
        });

        intStream.limit(20).forEach(System.out::println);
    }
}

2.使用map

Stream.map()是Stream最常用的一个转换方法,它把一个Stream转换为另一个Stream。
例如:
通过map对字符集合去空格、变小写:

/**
 * 通过map对字符集合去空格、变小写
 **/
public class StreamDemo {
    public static void main(String[] args) throws IOException {
        List<String> list=new ArrayList<>();
        list.add(" apple");
        list.add("Banana");
        list.add("ORANGE");
        Stream<String> stream = list.stream();

        stream.map(String::toLowerCase)
                .map(String::trim)
                .forEach(System.out::println);
    }
}

去除日期当中的空格并输出该日是本年的第几天:

/**
 * 去除日期当中的空格并输出该日是本年的第几天
 */
public class StreamDemo {
    public static void main(String[] args) throws IOException {
        String[] times = new String[]{"2020 - 04 - 01", "2022 - 04 - 14"};
        Stream<String> timesStream = Arrays.stream(times);
        timesStream.map(t->t.replaceAll(" ",""))
                .map(LocalDate::parse)
                .map(LocalDate::getDayOfYear)
                .forEach(System.out::println);
    }
}

将一组序列转换为它的立方:

/**
 * 将一组序列转换为它的立方
 */
public class StreamDemo {
    public static void main(String[] args) {
        IntStream intStream = IntStream.generate(new IntSupplier() {
            int n = 1;

            @Override
            public int getAsInt() {
                return n++;
            }
        });

        IntStream intStream1 = intStream.map(n -> n * n * n);
        intStream1.limit(5).forEach(System.out::println);
    }
}

3.使用filter

filter()操作就是对一个Stream的所有元素根据条件进行过滤,满足条件的元素就构成了一个新的Stream
例如:
判断元素是否是奇数,过滤掉偶数,只剩下奇数:

/**
 * 判断元素是否是奇数,过滤掉偶数,只剩下奇数
 */
public class StreamDemo {
    public static void main(String[] args) {
        IntStream intStream = IntStream.generate(new IntSupplier() {
            int n = 1;

            @Override
            public int getAsInt() {
                return n++;
            }
        });

        IntStream intStream1 = intStream.filter(n -> n % 2 != 0);
        intStream1.limit(5).forEach(System.out::println);
    }
}

从一组给定的LocalDate中过滤掉工作日,以便得到休息日:

/**
 * 从一组给定的LocalDate中过滤掉工作日,以便得到休息日
 */
public class StreamDemo {
    public static void main(String[] args) {
        Stream<LocalDate> stream = Stream.generate(new Supplier<LocalDate>() {
            LocalDate start = LocalDate.of(2022, 4, 1);
            int n = 0;

            @Override
            public LocalDate get() {
                return start.plusDays(n++);
            }
        });
        stream.limit(31)
                .filter(d -> d.getDayOfWeek() == DayOfWeek.SATURDAY
                        || d.getDayOfWeek() == DayOfWeek.SUNDAY)
                .forEach(System.out::println);
    }
}

4.使用reduce

reduce()则是Stream的一个聚合方法,它可以把一个Stream的所有元素按照聚合函数聚合成一个结果。
例如:
返回累加的结果

/**
 * 返回累加的结果
 */
public class StreamDemo {
    public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i+1);
        }

        Stream<Integer> stream = list.stream();
        Integer acc = stream.reduce(0, (sum, i) -> sum + i);
        System.out.println(acc);
    }
}

以上代码可能不太好理解,我们用另一种等价的形式改写一下
Stream<Integer> stream = list.stream();
int sum = 0;
for (n : stream) {
sum = (sum, n) -> sum + n;
}

举个例子,从配置文件读取数据并准换成Map:

public class StreamDemo {
    public static void main(String[] args) throws IOException {
    	//读取配置文件
        Stream<String> lines = Files.lines(Paths.get("D:\\properties.yaml"), Charset.forName("UTF-8"));
        //将读取到由每一行组成的Stream转成单个Map组成的Stream,然后聚合
        Map<String, String> result = lines.map(line -> {
                                        Map<String, String> map = new HashMap<>();
                                        String[] split = line.split("=");
                                        map.put(split[0], split[1]);
                                        return map;
                                    }).reduce(new HashMap<String, String>(), (map, it) -> {
                                        map.putAll(it);
                                        return map;
                                    });
        System.out.println(result);
    }
}

5.特性

执行以下代码:

    public static void main(String[] args) throws IOException, InterruptedException {
       IntStream stream=IntStream.generate(new IntSupplier() {
           int n=0;
           @Override
           public int getAsInt() {
               return n++;
           }
       });
        IntStream intStream = stream.limit(100000000).map(n -> n * n);
//        List<Integer> list = stream.limit(100000000).mapToObj(i -> i).collect(Collectors.toList());

        Thread.sleep(10000000);
    }

会发现没有太高的内存占用,也没有CPU占用
在这里插入图片描述
因为转换操作只是保存了转换规则,无论我们对一个Stream转换多少次,都不会有任何实际计算发生。

但如果运行以下代码:

public class StreamDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
       IntStream stream=IntStream.generate(new IntSupplier() {
           int n=0;
           @Override
           public int getAsInt() {
               return n++;
           }
       });
//        IntStream intStream = stream.limit(100000000).map(n -> n * n);
        List<Integer> list = stream.limit(100000000).mapToObj(i -> i).collect(Collectors.toList());

        Thread.sleep(10000000);
    }
}

会发现占用了大量内存和CPU
在这里插入图片描述
这是因为大量的数据存储在了List当中,占用了大量内存

6.输出为数组

    public static void main(String[] args) throws IOException, InterruptedException {
        IntStream stream = IntStream.generate(new IntSupplier() {
            int n = 0;

            @Override
            public int getAsInt() {
                return n++;
            }
        });
        int[] array = stream.limit(100).toArray();
        System.out.println(Arrays.toString(array));
    }

7.输出为List

    public static void main(String[] args) throws IOException, InterruptedException {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4);
        List<Integer> list = stream.collect(Collectors.toList());
        System.out.println(list);
    }

调用collect()并传入Collectors.toList()对象,通过类似reduce()的操作,把每个元素添加到ArrayList当中。

类似的,collect(Collectors.toSet())可以把Stream的每个元素收集到Set中。

        Stream<Integer> stream = Stream.of(1, 2, 3, 4);
        Set<Integer> set = stream.collect(Collectors.toSet());
        System.out.println(set);

8.输出为Map

public class StreamDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        Stream<String> stream = Stream.of("a:A", "b:B");
        Map<String, String> map = stream.collect(Collectors.toMap(
                s -> s.substring(0, s.indexOf(':')),
                s -> s.substring(s.indexOf(':') + 1)
        ));
        System.out.println(map);
    }
}

如此一来,第四节将配置文件转换成map的d代码就可以简化为

public class StreamDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        Stream<String> lines = Files.lines(Paths.get("D:\\properties.yaml"), Charset.forName("UTF-8"));
        Map<String, String> map = lines.collect(Collectors.toMap(
                line -> line.substring(0, line.indexOf('=')), // 配置文件里的key
                line -> line.substring(line.indexOf('=') + 1) //配置文件里的value
        ));
        System.out.println(map);
    }
}

9.分组

分组输出使用Collectors.groupingBy(),它需要提供两个函数:一个是分组的依据,第二个是分组的结果

public class StreamDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        Stream<String> stream
                = Stream.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots");
        Map<String, List<String>> result = stream.collect(Collectors.groupingBy(
                s -> s.substring(0, 1), // 表示按照首字母分组
                Collectors.toList() //表示表示输出为List
        ));
        System.out.println(result);
    }
}

10.其他操作

10.1 排序:

        Stream<Integer> stream=Stream.of(6,8,6,8,7,2,3,3,9,9,4,6,1);
        List<Integer> list = stream.sorted().collect(Collectors.toList());
        System.out.println(list);

此方法要求Stream的每个元素必须实现Comparable接口。
可自定义Comparable,传入sorted()。
以下sorted((i1, i2) -> i2.compareTo(i1))实现降序排序:

        Stream<Integer> stream = Stream.of(6, 8, 6, 8, 7, 2, 3, 3, 9, 9, 4, 6, 1);
        List<Integer> list = stream
                .sorted((i1, i2) -> i2.compareTo(i1))
                .collect(Collectors.toList());
        System.out.println(list);

10.2 去重

        Stream<Integer> stream = Stream.of(6, 8, 6, 8, 7, 2, 3, 3, 9, 9, 4, 6, 1);
        List<Integer> list = stream
                .distinct()  // 去重
                .collect(Collectors.toList());
        System.out.println(list);
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java流式编程常用API包括:java.util.stream.Streamjava.util.stream.IntStreamjava.util.stream.LongStreamjava.util.stream.DoubleStream等。 ### 回答2: Java流式编程常用的API有: 1. Stream APIJava 8引入的新特性,用于处理集合数据的功能。可以对集合进行筛选、排序、映射等操作,具有较高的灵活性和性能。 2. Collectors API:用于对Stream中的元素进行汇总的API。可以将元素收集到List、Set、Map等数据结构中,也可以进行分组、分区等操作。 3. Predicate API:用于筛选Stream中的元素。可以根据指定的条件,对元素进行过滤,返回满足条件的元素流。 4. Function API:用于对Stream中的元素进行转换的API。可以对元素进行映射、提取等操作,返回转换后的元素流。 5. Optional API:用于处理可能为空的数据的API。可以对可能为空的值进行安全处理,避免出现空指针异常。 6. Comparator API:用于比较对象的API。可以用于对集合中的元素进行排序,定义自定义的排序规则。 7. Iterator/Iterable API:用于遍历集合元素的API。可以使用Iterator对集合中的元素进行迭代访问,也可以通过实现Iterable接口实现自定义集合的遍历。 8. BufferedReader/BufferedWriter API:用于处理文本文件的输入输出的API。可以提高文件读写的效率,减少I/O操作的次数。 9. Files API:用于处理文件和目录的API。可以创建、复制、移动、删除文件及目录,以及判断文件是否存在等操作。 10. DateTime API:用于处理日期和时间的API。可以对日期和时间进行格式化、计算、比较等操作,提供了更加灵活和简便的操作方式。 ### 回答3: 以下是 Java 流式编程常用的 API: 1. Stream 类:Stream 类是 Java 8 引入的一个新特性,它提供了一种链式编程的方式来对集合进行操作。可以使用 Stream 类的各种方法来进行过滤、映射、排序、归约等操作,比如 filter、map、sorted、reduce 等。 2. Collectors 类:Collectors 类提供了一系列用于收集 Stream 中元素的静态方法。可以使用 Collectors 类提供的方法将 Stream 中的元素收集到一个集合中,比如 toList、toSet、toMap 等。 3. Optional 类:Optional 类是用来处理可能为空的值的。可以使用 Optional 类的方法来判断一个值是否存在,如果不存在,则可以使用默认值来替代。 4. forEach 方法:forEach 方法是 Stream 类的一个方法,可以对 Stream 中的每个元素进行遍历操作。可以使用 forEach 方法来遍历集合中的所有元素,并对每个元素执行指定的操作。 5. filter 方法:filter 方法用于对 Stream 中的元素进行过滤操作。可以使用 filter 方法来筛选出符合条件的元素,从而得到一个新的 Stream。 6. map 方法:map 方法用于对 Stream 中的元素进行映射操作。可以使用 map 方法将一个 Stream 中的元素转换为另一种类型,并得到一个新的 Stream。 7. sorted 方法:sorted 方法用于对 Stream 中的元素进行排序操作。可以使用 sorted 方法对 Stream 中的元素进行升序或者降序排序。 8. limit 方法:limit 方法用于对 Stream 进行截断操作。可以使用 limit 方法来限制 Stream 中元素的数量,从而得到一个新的 Stream。 这些都是 Java 流式编程常用的 API,它们提供了丰富的功能和灵活的操作方式,可以帮助开发者更方便地处理集合中的元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值