【Java语法小记】求字符串中某个字符的数量——IntStream流的使用

这篇具有很好参考价值的文章主要介绍了【Java语法小记】求字符串中某个字符的数量——IntStream流的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引入需求

从一段代码引入

return s.length() - (int) s.chars().filter(c -> c == 'S').count(); 

其中 (int) s.chars().filter(c -> c == 'S').count(); 计算了字符串 s 中字符 ‘S’ 的数量。
下面解读其原理:

代码原理解读

s.chars()

【Java语法小记】求字符串中某个字符的数量——IntStream流的使用,Java,java,流,IntStream,语法
Java 中的 String 类的 chars() 方法是用来将字符串转换为 IntStream 的一种方法。IntStream是一个表示 int 值序列的流。

该方法不接受任何参数,返回一个 IntStream,其中每个元素是字符串中对应位置的 char 值。

String s = "Hello";
IntStream chars = s.chars();
chars.forEach(System.out::println);

// 输出结果为
72
101
108
108
111

IntStream filter​(IntPredicate predicate)

本题中使用的是 IntStream 类的 .filter() 方法 (除此之外其它类有的也会有 .filter() 方法)
【Java语法小记】求字符串中某个字符的数量——IntStream流的使用,Java,java,流,IntStream,语法
Java 中的 .filter() 方法是一个中间操作,它会返回一个新的流,该流由该流中与给定 predicate 匹配的元素组成。(可以认为这是一个过滤器)

比如本题就是只保留了 c == 'S' 的元素。

long count()

【Java语法小记】求字符串中某个字符的数量——IntStream流的使用,Java,java,流,IntStream,语法

注意返回值是 long 基本数据类型。

Java 中的 .count() 方法是一个终端操作,它返回流中元素的数量。它是一种特殊的归约操作,它将一系列输入元素组合成一个单一的结果。例如,如果我们想要统计一个整数列表中有多少个偶数,我们可以这样写:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6); //整数列表
long count = list.stream() //创建一个流
    .filter(n -> n % 2 == 0) //根据谓词筛选出偶数
    .count(); //计算流中元素的数量
System.out.println(count); //输出结果 为 3

补充:IntStream peek​(IntConsumer action)

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/IntStream.html#peek(java.util.function.IntConsumer)
【Java语法小记】求字符串中某个字符的数量——IntStream流的使用,Java,java,流,IntStream,语法
返回一个由该流的元素组成的流,并在从结果流中消耗元素时对每个元素执行所提供的操作。

此方法的存在主要是为了支持调试

peek() 方法不会改变流中元素的值或顺序,也不会影响流的终端操作。它只是在流中插入了一个额外的操作,用于观察或记录元素。它返回一个新的 IntStream 对象,因此可以和其他的中间操作或终端操作链式调用。

可以看下面的代码示例1:

IntStream.of(1, 2, 3, 4)
        .filter(e -> e > 2)
        .peek(e -> System.out.println("Filtered value: " + e))
        .map(e -> e * e)
        .peek(e -> System.out.println("Mapped value: " + e))
        .sum();

// 控制台输出结果为:
Filtered value: 3
Mapped value: 9
Filtered value: 4
Mapped value: 16

代码示例2:

IntStream.of(5, 3, 1, 4, 2) //创建一个整数流
        .peek(n -> System.out.println("Original: " + n)) //打印原始值
        .sorted() //排序
        .peek(n -> System.out.println("Sorted: " + n)) //打印排序后的值
        .sum(); //求和
        
// 控制台输出结果为:
Original: 5
Original: 3
Original: 1
Original: 4
Original: 2
Sorted: 1
Sorted: 2
Sorted: 3
Sorted: 4
Sorted: 5

这时候会有疑问:为什么两段代码示例的控制台输出结果顺序好像不符合预期
A:这是因为流的操作是惰性的,也就是说,只有当终端操作(如 sum() )需要时,才会真正执行中间操作(如 filter() , map() , peek() )
因此,流中的每个元素都会按照管道中的顺序依次执行所有的中间操作,而不是先执行完一个中间操作再执行下一个。所以,在代码示例 1 中,对于第一个元素3,它会先被 filter() ,然后被 peek() ,然后被 map() ,然后再被 peek() ,最后才会被 sum() 。对于第二个元素4,它也会经历同样的过程。因此,Mapped value: 9 会输出在 Filtered value: 4 之前。


在上文中 count() 方法的文档中有这样一段代码:

IntStream s = IntStream.of(1, 2, 3, 4);
long count = s.peek(System.out::println).count();

这段代码对应着控制台没有任何输出,这是因为count() 方法是一个短路操作,也就是说,它不需要遍历所有的元素就可以得到结果。因此,对于一个有限的流, count() 方法会直接返回流中元素的数量,而不会触发任何中间操作(如 peek() )。这是 Java 9 中对 count() 方法的一个优化,以提高性能。

流操作和管道

Stream operations and pipelines
这部分的英文原文如下:

Stream operations are divided into intermediate and terminal operations, and are combined to form stream pipelines. A stream pipeline consists of a source (such as a Collection, an array, a generator function, or an I/O channel); followed by zero or more intermediate operations such as Stream.filter or Stream.map; and a terminal operation such as Stream.forEach or Stream.reduce.

Intermediate operations return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.

Terminal operations, such as Stream.forEach or IntStream.sum, may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream. In almost all cases, terminal operations are eager, completing their traversal of the data source and processing of the pipeline before returning. Only the terminal operations iterator() and spliterator() are not; these are provided as an “escape hatch” to enable arbitrary client-controlled pipeline traversals in the event that the existing operations are not sufficient to the task.

Processing streams lazily allows for significant efficiencies; in a pipeline such as the filter-map-sum example above, filtering, mapping, and summing can be fused into a single pass on the data, with minimal intermediate state. Laziness also allows avoiding examining all the data when it is not necessary; for operations such as “find the first string longer than 1000 characters”, it is only necessary to examine just enough strings to find one that has the desired characteristics without examining all of the strings available from the source. (This behavior becomes even more important when the input stream is infinite and not merely large.)

Intermediate operations are further divided into stateless and stateful operations. Stateless operations, such as filter and map, retain no state from previously seen element when processing a new element – each element can be processed independently of operations on other elements. Stateful operations, such as distinct and sorted, may incorporate state from previously seen elements when processing new elements.

Stateful operations may need to process the entire input before producing a result. For example, one cannot produce any results from sorting a stream until one has seen all elements of the stream. As a result, under parallel computation, some pipelines containing stateful intermediate operations may require multiple passes on the data or may need to buffer significant data. Pipelines containing exclusively stateless intermediate operations can be processed in a single pass, whether sequential or parallel, with minimal data buffering.

Further, some operations are deemed short-circuiting operations. An intermediate operation is short-circuiting if, when presented with infinite input, it may produce a finite stream as a result. A terminal operation is short-circuiting if, when presented with infinite input, it may terminate in finite time. Having a short-circuiting operation in the pipeline is a necessary, but not sufficient, condition for the processing of an infinite stream to terminate normally in finite time.


中文翻译如下:(这部分信息量很大很重要!

流操作分为中间操作终端操作,并结合起来形成流管道。流管道由一个源(例如Collection、数组、生成器函数或I/O通道)组成;然后是零个或多个中间操作,例如Stream.filter或Stream.map;以及诸如Stream.forEach或Stream.reduce之类的终端操作。

中间操作返回一个新流。他们总是懒惰;执行诸如filter()之类的中间操作实际上并不执行任何过滤,而是创建一个新流,当遍历该新流时,该新流包含与给定谓词匹配的初始流的元素。在执行管道的终端操作之前,管道源的遍历不会开始

终端操作,如Stream.forEach或IntStream.sum,可能会遍历流以产生结果或副作用。在执行终端操作之后,流管道被认为已被消耗,并且不能再使用;如果需要再次遍历同一数据源,则必须返回到数据源以获得新的流。在几乎所有情况下,终端操作都很渴望,在返回之前完成对数据源的遍历和对管道的处理。只有终端操作迭代器()和拆分器()不是;这些是作为“逃生通道”提供的,以便在现有操作不足以完成任务的情况下,实现任意客户端控制的管道遍历。

懒散地处理流可以实现显著的效率;在像上面的filter map sum示例这样的流水线中,过滤、映射和求和可以被融合到数据的单个传递中,具有最小的中间状态懒惰还可以避免在不必要的时候检查所有数据;对于诸如“查找长度超过1000个字符的第一个字符串”之类的操作,只需检查刚好足够的字符串即可找到具有所需特性的字符串,而无需检查源中所有可用的字符串。(当输入流是无限的而不仅仅是大的时,这种行为变得更加重要。)

中间操作进一步分为无状态操作有状态操作。无状态操作,如filter和map,在处理新元素时不会保留以前看到的元素的状态——每个元素都可以独立于对其他元素的操作进行处理。在处理新元素时,有状态的操作(如distinct和sorted)可以合并以前看到的元素的状态。

有状态操作可能需要在生成结果之前处理整个输入。例如,在看到流的所有元素之前,对流进行排序无法产生任何结果。因此,在并行计算下,一些包含有状态中间操作的管道可能需要对数据进行多次传递,或者可能需要缓冲重要数据。包含完全无状态中间操作的管道可以在一次过程中处理,无论是顺序的还是并行的,只需最少的数据缓冲。

此外,一些操作被认为是短路操作。如果在无限输入的情况下,中间运算可能会产生有限的流,那么它就是短路。如果在无限输入的情况下,终端操作可能在有限时间内终止,则终端操作是短路。在管道中进行短路操作是处理无限流在有限时间内正常终止的必要条件,但不是充分条件。文章来源地址https://www.toymoban.com/news/detail-521682.html


到了这里,关于【Java语法小记】求字符串中某个字符的数量——IntStream流的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java判断某个字符串是否在字符串数组中的方法(4种)

    public class Demo {     public static boolean useLoop(String[] arr, String targetValue) {         for (String s : arr) {             if (s.equals(targetValue)) return true;         }         return false;     }       public static void main(String[] args) {         String arr[] = {\\\"aa\\\", \\\"bb\\\", \\\"cc\\\"};       

    2024年02月11日
    浏览(95)
  • java 怎么判断某个字符串是否在数组中

    在 Java 中,可以使用以下几种方法来判断一个字符串是否在数组中: 使用 for 循环遍历数组,逐个比较字符串是否相等。 使用 Arrays.asList() 将数组转换为列表,然后使用列表的 contains() 方法来判断字符串是否在数组中。 使用 Java 8 的 Stream API,使用 anyMatch() 方法来判断是否存

    2024年02月14日
    浏览(92)
  • js如何截取某个字符串前面所有的字符串

    利用substring()方法截取出字符 1.新建一个字符串 2.通过indexOf()方法获取你想截止到的那个字符 3.通过substring()方法,从字符串0位置开始截取至\\\"p\\\"前面的字符串

    2024年02月12日
    浏览(51)
  • JS中判断某个字符串、数组等是否包含某个值

    方法一: indexOf()   (推荐)   indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。如果要检索的字符串值没有出现,则该方法返回 -1。 方法二:match()   match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。 方法三: search()   search() 方法

    2024年02月03日
    浏览(107)
  • SwiftUI 如何保证 Text 中字符数量相等的字符串显示宽度一定相同?

    在 SwiftUI 中我们往往需要将内容相似的字符串展列出来给用户比较,这些字符串内容各有不同但字符数量始终是相等的,我们希望它们的显示宽度始终保持一致: 如上图所示:即使是等宽字符组成的字符串在字符数量相等时它们的显示宽度仍然可能不一致。但演示中最底部的

    2024年01月17日
    浏览(37)
  • 【Qt】QString怎么判断是否含有某个字符串

    2023年10月29日,周日晚上 QString怎么判断是否含有某一字符串? 比如我想知道QString中是否含有\\\"connectToHost error\\\" 要判断一个 QString 是否包含特定的子字符串,可以使用  QString  类的  contains()  方法。 下面是使用  contains()  方法来判断一个 QString 是否包含特定子字符串的示例

    2024年02月06日
    浏览(80)
  • C#用正则表达式获取字符串中汉字的数量

    目录 一、关于字符串和判断其组成常识 二、用正则表达式获取字符串中汉字的数量 1.字符是否为汉字的正则表达式 2.实例 3.生成结果 三、相关知识点 1.Regex.IsMatch 方法          字符串中可以包括数字、字母、汉字或者其他字符。使用Char类型的IsDigit静态方法可以判断字

    2024年01月17日
    浏览(43)
  • sqlserver 查找某个字符在字符串中第N次出现的位置

    如果想要在 Microsoft SQL Server 中查找某个字符在字符串中第 N 次出现的位置,可以使用 CHARINDEX 函数。该函数接受三个参数: 要查找的字符(必需) 要搜索的字符串(必需) 开始搜索的位置(可选) 它会返回所查找字符在字符串中的位置,如果字符不存在,则返回 0。 举个例子,如果

    2024年02月13日
    浏览(37)
  • linux命令统计文件中某个字符串出现的次数

    可以使用grep命令统计文件中某个字符串出现的次数。语法: - grep -o:查找文件中匹配的字符串,并只输出字符串本身 - \\\'字符串\\\':需要统计的字符串,用单引号括起来 - 文件名:需要查找的文件的路径和文件名 - wc -l:统计行数,这里统计grep输出的行数,即字符串出现的次数例如,要统计

    2024年02月08日
    浏览(42)
  • 蓝桥杯专题-试题版-【圆的面积】【字符串对比】【字母图形】【核桃的数量】

    点击跳转专栏=Unity3D特效百例 点击跳转专栏=案例项目实战源码 点击跳转专栏=游戏脚本-辅助自动化 点击跳转专栏=Android控件全解手册 点击跳转专栏=Scratch编程案例 点击跳转=软考全系列 点击跳转=蓝桥系列 专注于 Android/Unity 和各种游戏开发技巧,以及 各种资源分享 (网站、

    2024年02月12日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包