Java 8 API 示例:字符串、數值、算術和文件

Java 8 API 示例:字符串、數值、算術和文件

原文:Java 8 API by Example: Strings, Numbers, Math and Files java

譯者:飛龍 git

協議:CC BY-NC-SA 4.0github

大量的教程和文章都涉及到Java8中最重要的改變,例如lambda表達式函數式數據流。可是此外許多現存的類在JDK 8 API中也有所改進,帶有一些實用的特性和方法。正則表達式

這篇教程涉及到Java 8 API中的那些小修改 -- 每一個都使用簡單易懂的代碼示例來描述。讓咱們好好看一看字符串、數值、算術和文件。編程

處理字符串

兩個新的方法可在字符串類上使用:joinchars。第一個方法使用指定的分隔符,將任何數量的字符串鏈接爲一個字符串。api

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

此外,正則模式串能夠轉換爲謂詞。這些謂詞能夠像下面那樣用於過濾字符串流:post

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

上面的模式串接受任何以@gmail.com結尾的字符串,而且以後用做Java8的Predicate來過濾電子郵件地址流。code

處理數值

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方法將指定目錄的全部路徑轉換爲數據流,便於咱們在文件系統的內容上使用相似filtersorted的流操做。

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語句來關閉文件流,這會使示例代碼有些凌亂。我期待函數式數據流能夠在調用相似countcollect時能夠自動關閉,由於你不能在相同數據流上調用終止操做兩次。

我但願你能喜歡這篇文章。全部示例代碼都託管在Github上,還有來源於我博客其它Java8文章的大量的代碼片斷。若是這篇文章對你有所幫助,請收藏個人倉庫,而且在Twitter上關注我

請堅持編程!

相關文章
相關標籤/搜索