Java 8 API 示例:字符串、数值、算术和文件

Java 8 API 示例:字符串、数值、算术和文件

大量的教程和文章都涉及到Java8中最重要的改变,例如lambda表达式函数式数据流。但是此外许多现存的类在JDK 8 API中也有所改进,带有一些实用的特性和方法。

这篇教程涉及到Java 8 API中的那些小修改 -- 每个都使用简单易懂的代码示例来描述。让我们好好看一看字符串、数值、算术和文件。

处理字符串

两个新的方法可在字符串类上使用:join和chars。第一个方法使用指定的分隔符,将任何数量的字符串连接为一个字符串。

String.join(":", "foobar", "foo", "bar");
// => foobar:foo:bar

第二个方法chars从字符串所有字符创建数据流,所以你可以在这些字符上使用流式操作。

"foobar:foo:bar"
    .chars()
    .distinct()
    .mapToObj(c -> String.valueOf((char)c))
    .sorted()
    .collect(Collectors.joining());
// => :abfor

不仅仅是字符串,正则表达式模式串也能受益于数据流。我们可以分割任何模式串,并创建数据流来处理它们,而不是将字符串分割为单个字符的数据流,像下面这样:

Pattern.compile(":")
    .splitAsStream("foobar:foo:bar")
    .filter(s -> s.contains("bar"))
    .sorted()
    .collect(Collectors.joining(":"));
// => bar:foobar

此外,正则模式串可以转换为谓词。这些谓词可以像下面那样用于过滤字符串流:

Pattern pattern = Pattern.compile(".*@gmail\\.com");
Stream.of("bob@gmail.com", "alice@hotmail.com")
    .filter(pattern.asPredicate())
    .count();
// => 1

上面的模式串接受任何以@gmail.com结尾的字符串,并且之后用作Java8的Predicate来过滤电子邮件地址流。

处理数值

Java8添加了对无符号数的额外支持。Java中的数值总是有符号的,例如,让我们来观察Integer:

int可表示最多2 ** 32个数。Java中的数值默认为有符号的,所以最后一个二进制数字表示符号(0为正数,1为负数)。所以从十进制的0开始,最大的有符号正整数为2 ** 31 - 1。

你可以通过Integer.MAX_VALUE来访问它:

System.out.println(Integer.MAX_VALUE);      // 2147483647
System.out.println(Integer.MAX_VALUE + 1);  // -2147483648

Java8添加了解析无符号整数的支持,让我们看看它如何工作:

long maxUnsignedInt = (1l << 32) - 1;
String string = String.valueOf(maxUnsignedInt);
int unsignedInt = Integer.parseUnsignedInt(string, 10);
String string2 = Integer.toUnsignedString(unsignedInt, 10);

就像你看到的那样,现在可以将最大的无符号数2 ** 32 - 1解析为整数。而且你也可以将这个数值转换回无符号数的字符串表示。

这在之前不可能使用parseInt完成,就像这个例子展示的那样:

try {
    Integer.parseInt(string, 10);
}
catch (NumberFormatException e) {
    System.err.println("could not parse signed int of " + maxUnsignedInt);
}

这个数值不可解析为有符号整数,因为它超出了最大范围2 ** 31 - 1。

算术运算

Math工具类新增了一些方法来处理数值溢出。这是什么意思呢?我们已经看到了所有数值类型都有最大值。所以当算术运算的结果不能被它的大小装下时,会发生什么呢?

System.out.println(Integer.MAX_VALUE);      // 2147483647
System.out.println(Integer.MAX_VALUE + 1);  // -2147483648

就像你看到的那样,发生了整数溢出,这通常是我们不愿意看到的。

Java8添加了严格数学运算的支持来解决这个问题。Math扩展了一些方法,它们全部以exact结尾,例如addExact。当运算结果不能被数值类型装下时,这些方法通过抛出ArithmeticException异常来合理地处理溢出。

try {
    Math.addExact(Integer.MAX_VALUE, 1);
}
catch (ArithmeticException e) {
    System.err.println(e.getMessage());
    // => integer overflow
}

当尝试通过toIntExact将长整数转换为整数时,可能会抛出同样的异常:

try {
    Math.toIntExact(Long.MAX_VALUE);
}
catch (ArithmeticException e) {
    System.err.println(e.getMessage());
    // => integer overflow
}

处理文件

Files工具类首次在Java7中引入,作为NIO的一部分。JDK8 API添加了一些额外的方法,它们可以将文件用于函数式数据流。让我们深入探索一些代码示例。

列出文件

Files.list方法将指定目录的所有路径转换为数据流,便于我们在文件系统的内容上使用类似filter和sorted的流操作。

try (Stream<Path> stream = Files.list(Paths.get(""))) {
    String joined = stream
        .map(String::valueOf)
        .filter(path -> !path.startsWith("."))
        .sorted()
        .collect(Collectors.joining("; "));
    System.out.println("List: " + joined);
}

上面的例子列出了当前工作目录的所有文件,之后将每个路径都映射为它的字符串表示。之后结果被过滤、排序,最后连接为一个字符串。如果你还不熟悉函数式数据流,你应该阅读我的Java8数据流教程

你可能已经注意到,数据流的创建包装在try-with语句中。数据流实现了AutoCloseable,并且这里我们需要显式关闭数据流,因为它基于IO操作。

返回的数据流是DirectoryStream的封装。如果需要及时处理文件资源,就应该使用try-with结构来确保在流式操作完成后,数据流的close方法被调用。

查找文件

下面的例子演示了如何查找在目录及其子目录下的文件:

Path start = Paths.get("");
int maxDepth = 5;
try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) ->
        String.valueOf(path).endsWith(".js"))) {
    String joined = stream
        .sorted()
        .map(String::valueOf)
        .collect(Collectors.joining("; "));
    System.out.println("Found: " + joined);
}

find方法接受三个参数:目录路径start是起始点,maxDepth定义了最大搜索深度。第三个参数是一个匹配谓词,定义了搜索的逻辑。上面的例子中,我们搜索了所有JavaScirpt文件(以.js结尾的文件名)。

我们可以使用Files.walk方法来完成相同的行为。这个方法会遍历每个文件,而不需要传递搜索谓词。

Path start = Paths.get("");
int maxDepth = 5;
try (Stream<Path> stream = Files.walk(start, maxDepth)) {
    String joined = stream
        .map(String::valueOf)
        .filter(path -> path.endsWith(".js"))
        .sorted()
        .collect(Collectors.joining("; "));
    System.out.println("walk(): " + joined);
}

这个例子中,我们使用了流式操作filter来完成和上个例子相同的行为。

读写文件

将文本文件读到内存,以及向文本文件写入字符串在Java 8 中是简单的任务。不需要再去摆弄读写器了。Files.readAllLines从指定的文件把所有行读进字符串列表中。你可以简单地修改这个列表,并且将它通过Files.write写到另一个文件中:

List<String> lines = Files.readAllLines(Paths.get("res/nashorn1.js"));
lines.add("print('foobar');");
Files.write(Paths.get("res/nashorn1-modified.js"), lines);

要注意这些方法对内存并不十分高效,因为整个文件都会读进内存。文件越大,所用的堆区也就越大。

你可以使用Files.lines方法来作为内存高效的替代。这个方法读取每一行,并使用函数式数据流来对其流式处理,而不是一次性把所有行都读进内存。

try (Stream<String> stream = Files.lines(Paths.get("res/nashorn1.js"))) {
    stream
        .filter(line -> line.contains("print"))
        .map(String::trim)
        .forEach(System.out::println);
}

如果你需要更多的精细控制,你需要构造一个新的BufferedReader来代替:

Path path = Paths.get("res/nashorn1.js");
try (BufferedReader reader = Files.newBufferedReader(path)) {
    System.out.println(reader.readLine());
}

或者,你需要写入文件时,简单地构造一个BufferedWriter来代替:

Path path = Paths.get("res/output.js");
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
    writer.write("print('Hello World');");
}

BufferedReader也可以访问函数式数据流。lines方法在它所有行上面构建数据流:

Path path = Paths.get("res/nashorn1.js");
try (BufferedReader reader = Files.newBufferedReader(path)) {
    long countPrints = reader
        .lines()
        .filter(line -> line.contains("print"))
        .count();
    System.out.println(countPrints);
}

目前为止你可以看到Java8提供了三个简单的方法来读取文本文件的每一行,使文件处理更加便捷。

不幸的是你需要显式使用try-with语句来关闭文件流,这会使示例代码有些凌乱。我期待函数式数据流可以在调用类似count和collect时可以自动关闭,因为你不能在相同数据流上调用终止操作两次。

Image placeholder
someone
未设置
  0人点赞

没有讨论,发表一下自己的看法吧

推荐文章
Bash技巧:使用参数扩展获取变量的子字符串和字符串长度

在bash中,通常使用${parameter}表达式来获取parameter变量的值,这是一种参数扩展(parameterexpansion)。Bash还提供了其他形式的参数扩展,可以对变量值做一些处

Go 语言数据类型:字符类型

字符类型 字符基本输入与输出 所谓字符类型是用单引号括起来的单个字符,关于字符的定义使用如下: funcmain(){ //声明变量 varchbyte//声明字符类型 chbyte='a'//单引号

算术运算符实现秒数拆分

提示用户输入整数类型的秒数并使用变量记录 将秒数转换为时分秒并使用变量单独记录 打印最终的转换结果

jquery如何判断字符串是否包含指定字符?

方法一:使用indexOf()和lastIndexOf()方法案例:varCts="bblText"; if(Cts.indexOf("Text")>=0){ alert('Cts中包含Text字符串

Laravel-Binlog 扩展(用于实时监听 MySQL 数据变更、数据同步等场景)

Laravel-Binlogv0.2.1 (该扩展当前用于我司测试环境实时同步Mysql数据变更到ElasticSearch,稳定性待测试!!哈哈哈)我司正式环境走的阿里云DTS数据订阅 基于Sw

高可用架构实例:在多云和多区域中穿行

Auth0为所有技术栈上的任一平台(移动,Web,本机)应用程序提供身份验证,授权和单点登录服务。身份验证对绝大多数应用程序至关重要。我们从一开始就设计了Auth0,以便它可以在任何地方运行:在我们的

Dubbo 稳定性案例:Nacos 注册中心可用性问题复盘

问题描述上周四晚刚回到家,就接到了软负载同学的电话,说是客户线上出了故障,我一听”故障“两个字,立马追问是什么情况,经过整理,还原出线上问题的原貌:客户使用了Dubbo,注册中心使用的是Nacos,在

基于内存和文件存储的 queue worker, 不用 Redis 适合单进程使用没有外部依赖

因为最近要做一个简单的并发任务系统,在github上面找了一圈并没有简单可依赖的库,所以自己写了一个。欢迎大家Review贡献代码。项目地址https://github.com/iflamed/mfw

GoWeb教程_07.6. 字符串处理

字符串在我们平常的Web开发中经常用到,包括用户的输入,数据库读取的数据等,我们经常需要对字符串进行分割、连接、转换等操作,本小节将通过Go标准库中的strings和strconv两个包中的函数来讲解

分享 6 个 Go 处理字符串的技巧

如果你从Ruby或者Python转型到Go,将会有很多语言差异需要学习,其中很多问题都是围绕处理string类型。下面是一些字符串的技巧,这些技巧解决了我在使用Golang的最初几周中遇到的问题。

Go语言高级编程_1.3 数组、字符串和切片

1.3数组、字符串和切片 在主流的编程语言中数组及其相关的数据结构是使用得最为频繁的,只有在它(们)不能满足时才会考虑链表、hash表(hash表可以看作是数组和链表的混合体)和更复杂的自定义数据结构

算法题:判断括号字符串是否有效

题目来源于力扣 理论基础 堆栈&队列 判断括号字符串是否有效 题目描述 给定一个只包括'(',')','{','}','[',']'的字符串,判断字符串是否有效。 有效字符串需满足:左括号必

算法题:判断括号字符串是否有效

题目来源于力扣 理论基础 堆栈&队列 判断括号字符串是否有效 题目描述 给定一个只包括'(',')','{','}','[',']'的字符串,判断字符串是否有效。 有效字符串需满足:左括号必

PHP 开发工程师基础篇 - PHP 字符串

PHP开发工程师基础篇-PHP字符串字符串(String)字符串是一系列字符的集合.如"abc".在PHP中,一个字符代表一个字节,一个字节(Byte)有8比特(bit).PHP仅支持256字符集,因

Stack Overflow 上 370万浏览量的一个问题:如何比较 Java 的字符串?

在逛StackOverflow的时候,发现了一些访问量像喜马拉雅山一样高的问题,比如说这个:如何比较Java的字符串?访问量足足有370万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。PS:

字符串类型

String的声明:变量值需要用引号(单、双、三引号)包装起来。声明方式的选择:如果字符串中有双引号,推荐使用单引号声明(HTML代码)。如果字符串中有单引号,推荐使用双引号声明(PHP,python

字符串

字符串加号‘+’做字符连接运算(只能同类运算) 字符串赋值运算乘号‘*’做字符串重复累计运算。复制的次数必须是整形而不能是浮点类型。 字符串索引操作,只需要将字符串当作列表操作,填入下标即可,也可做列

redis数据结构(二) - 字符串

基于redis5.0的版本。字符串编码:字符串对象的编码可以是int,raw或者embstr。1.rawraw就是redisObject+sds,即redisObject的ptr指针指向一个sds对象

java字符串“我是谁”的转码问题

微信公众号:爱问CTO专业编程问答社区www.askcto.com 问题出场publicstaticvoidmain(String[]args)throwsException{ Stringa="我是

jquery如何去除字符串空格?

1、使用trim()trim()函数用于去除字符串两端的空白字符。作用该函数可以去除字符串开始和末尾两端的空白字符(直到遇到第一个非空白字符串为止)。它会清除包括换行符、空格、制表符等常见的空白字符。

php常用字符串查找函数strstr()与strpos()实例分析

这篇文章主要介绍了php常用字符串查找函数strstr()与strpos(),结合具体实例形式分析了php字符串查找函数strstr()与strpos()的具体功能、用法、区别及相关操作注意事项,需要

jquery如何判断字符串是否相等?

在jquery中字符串比较没有equals()方法,要比较两个字符串是否相等可以直接用==来进行判断。有时需要注意空格!可以先使用$.trim(str)去掉字符串起始和结尾的空格,然后使用用==来进行

数据类型详解-字符串笔记

数据类型详解-字符串 回顾字符串的定义方式 了解转义字符 字符串相关的操作 字符串格式化的方法 字符串相关函数 字符串的定义方式 单引号定义字符串‘’ 双引号定义字符串“” 三引号定义字符串‘’

使用Jupyter NoteBook进行IB查询和交易,以及使用算法交易示例

在搞好IB盈透接口后,试了下客户端交易,但是最终目的还是使用程序化交易。发现vnpy已经提供的Script_engine来支持JupyterNoteBook交易的,而且非常方便调用。 这里就用写了基于