摘要:示例字符串?dāng)?shù)值算術(shù)和文件原文譯者飛龍協(xié)議大量的教程和文章都涉及到中最重要的改變,例如表達(dá)式和函數(shù)式數(shù)據(jù)流。不僅僅是字符串,正則表達(dá)式模式串也能受益于數(shù)據(jù)流。
Java 8 API 示例:字符串、數(shù)值、算術(shù)和文件
原文:Java 8 API by Example: Strings, Numbers, Math and Files
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
大量的教程和文章都涉及到Java8中最重要的改變,例如lambda表達(dá)式和函數(shù)式數(shù)據(jù)流。但是此外許多現(xiàn)存的類在JDK 8 API中也有所改進(jìn),帶有一些實(shí)用的特性和方法。
這篇教程涉及到Java 8 API中的那些小修改 -- 每個(gè)都使用簡(jiǎn)單易懂的代碼示例來(lái)描述。讓我們好好看一看字符串、數(shù)值、算術(shù)和文件。
處理字符串兩個(gè)新的方法可在字符串類上使用:join和chars。第一個(gè)方法使用指定的分隔符,將任何數(shù)量的字符串連接為一個(gè)字符串。
String.join(":", "foobar", "foo", "bar"); // => foobar:foo:bar
第二個(gè)方法chars從字符串所有字符創(chuàng)建數(shù)據(jù)流,所以你可以在這些字符上使用流式操作。
"foobar:foo:bar" .chars() .distinct() .mapToObj(c -> String.valueOf((char)c)) .sorted() .collect(Collectors.joining()); // => :abfor
不僅僅是字符串,正則表達(dá)式模式串也能受益于數(shù)據(jù)流。我們可以分割任何模式串,并創(chuàng)建數(shù)據(jù)流來(lái)處理它們,而不是將字符串分割為單個(gè)字符的數(shù)據(jù)流,像下面這樣:
Pattern.compile(":") .splitAsStream("foobar:foo:bar") .filter(s -> s.contains("bar")) .sorted() .collect(Collectors.joining(":")); // => bar:foobar
此外,正則模式串可以轉(zhuǎn)換為謂詞。這些謂詞可以像下面那樣用于過(guò)濾字符串流:
Pattern pattern = Pattern.compile(".*@gmail.com"); Stream.of("[email protected]", "[email protected]") .filter(pattern.asPredicate()) .count(); // => 1
上面的模式串接受任何以@gmail.com結(jié)尾的字符串,并且之后用作Java8的Predicate來(lái)過(guò)濾電子郵件地址流。
處理數(shù)值Java8添加了對(duì)無(wú)符號(hào)數(shù)的額外支持。Java中的數(shù)值總是有符號(hào)的,例如,讓我們來(lái)觀察Integer:
int可表示最多2 ** 32個(gè)數(shù)。Java中的數(shù)值默認(rèn)為有符號(hào)的,所以最后一個(gè)二進(jìn)制數(shù)字表示符號(hào)(0為正數(shù),1為負(fù)數(shù))。所以從十進(jìn)制的0開始,最大的有符號(hào)正整數(shù)為2 ** 31 - 1。
你可以通過(guò)Integer.MAX_VALUE來(lái)訪問(wèn)它:
System.out.println(Integer.MAX_VALUE); // 2147483647 System.out.println(Integer.MAX_VALUE + 1); // -2147483648
Java8添加了解析無(wú)符號(hào)整數(shù)的支持,讓我們看看它如何工作:
long maxUnsignedInt = (1l << 32) - 1; String string = String.valueOf(maxUnsignedInt); int unsignedInt = Integer.parseUnsignedInt(string, 10); String string2 = Integer.toUnsignedString(unsignedInt, 10);
就像你看到的那樣,現(xiàn)在可以將最大的無(wú)符號(hào)數(shù)2 ** 32 - 1解析為整數(shù)。而且你也可以將這個(gè)數(shù)值轉(zhuǎn)換回?zé)o符號(hào)數(shù)的字符串表示。
這在之前不可能使用parseInt完成,就像這個(gè)例子展示的那樣:
try { Integer.parseInt(string, 10); } catch (NumberFormatException e) { System.err.println("could not parse signed int of " + maxUnsignedInt); }
這個(gè)數(shù)值不可解析為有符號(hào)整數(shù),因?yàn)樗隽俗畲蠓秶?b>2 ** 31 - 1。
算術(shù)運(yùn)算Math工具類新增了一些方法來(lái)處理數(shù)值溢出。這是什么意思呢?我們已經(jīng)看到了所有數(shù)值類型都有最大值。所以當(dāng)算術(shù)運(yùn)算的結(jié)果不能被它的大小裝下時(shí),會(huì)發(fā)生什么呢?
System.out.println(Integer.MAX_VALUE); // 2147483647 System.out.println(Integer.MAX_VALUE + 1); // -2147483648
就像你看到的那樣,發(fā)生了整數(shù)溢出,這通常是我們不愿意看到的。
Java8添加了嚴(yán)格數(shù)學(xué)運(yùn)算的支持來(lái)解決這個(gè)問(wèn)題。Math擴(kuò)展了一些方法,它們?nèi)恳?b>exact結(jié)尾,例如addExact。當(dāng)運(yùn)算結(jié)果不能被數(shù)值類型裝下時(shí),這些方法通過(guò)拋出ArithmeticException異常來(lái)合理地處理溢出。
try { Math.addExact(Integer.MAX_VALUE, 1); } catch (ArithmeticException e) { System.err.println(e.getMessage()); // => integer overflow }
當(dāng)嘗試通過(guò)toIntExact將長(zhǎng)整數(shù)轉(zhuǎn)換為整數(shù)時(shí),可能會(huì)拋出同樣的異常:
try { Math.toIntExact(Long.MAX_VALUE); } catch (ArithmeticException e) { System.err.println(e.getMessage()); // => integer overflow }處理文件
Files工具類首次在Java7中引入,作為NIO的一部分。JDK8 API添加了一些額外的方法,它們可以將文件用于函數(shù)式數(shù)據(jù)流。讓我們深入探索一些代碼示例。
列出文件Files.list方法將指定目錄的所有路徑轉(zhuǎn)換為數(shù)據(jù)流,便于我們?cè)谖募到y(tǒng)的內(nèi)容上使用類似filter和sorted的流操作。
try (Streamstream = Files.list(Paths.get(""))) { String joined = stream .map(String::valueOf) .filter(path -> !path.startsWith(".")) .sorted() .collect(Collectors.joining("; ")); System.out.println("List: " + joined); }
上面的例子列出了當(dāng)前工作目錄的所有文件,之后將每個(gè)路徑都映射為它的字符串表示。之后結(jié)果被過(guò)濾、排序,最后連接為一個(gè)字符串。如果你還不熟悉函數(shù)式數(shù)據(jù)流,你應(yīng)該閱讀我的Java8數(shù)據(jù)流教程。
你可能已經(jīng)注意到,數(shù)據(jù)流的創(chuàng)建包裝在try-with語(yǔ)句中。數(shù)據(jù)流實(shí)現(xiàn)了AutoCloseable,并且這里我們需要顯式關(guān)閉數(shù)據(jù)流,因?yàn)樗贗O操作。
查找文件返回的數(shù)據(jù)流是DirectoryStream的封裝。如果需要及時(shí)處理文件資源,就應(yīng)該使用try-with結(jié)構(gòu)來(lái)確保在流式操作完成后,數(shù)據(jù)流的close方法被調(diào)用。
下面的例子演示了如何查找在目錄及其子目錄下的文件:
Path start = Paths.get(""); int maxDepth = 5; try (Streamstream = 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方法接受三個(gè)參數(shù):目錄路徑start是起始點(diǎn),maxDepth定義了最大搜索深度。第三個(gè)參數(shù)是一個(gè)匹配謂詞,定義了搜索的邏輯。上面的例子中,我們搜索了所有JavaScirpt文件(以.js結(jié)尾的文件名)。
我們可以使用Files.walk方法來(lái)完成相同的行為。這個(gè)方法會(huì)遍歷每個(gè)文件,而不需要傳遞搜索謂詞。
Path start = Paths.get(""); int maxDepth = 5; try (Streamstream = Files.walk(start, maxDepth)) { String joined = stream .map(String::valueOf) .filter(path -> path.endsWith(".js")) .sorted() .collect(Collectors.joining("; ")); System.out.println("walk(): " + joined); }
這個(gè)例子中,我們使用了流式操作filter來(lái)完成和上個(gè)例子相同的行為。
讀寫文件將文本文件讀到內(nèi)存,以及向文本文件寫入字符串在Java 8 中是簡(jiǎn)單的任務(wù)。不需要再去擺弄讀寫器了。Files.readAllLines從指定的文件把所有行讀進(jìn)字符串列表中。你可以簡(jiǎn)單地修改這個(gè)列表,并且將它通過(guò)Files.write寫到另一個(gè)文件中:
Listlines = Files.readAllLines(Paths.get("res/nashorn1.js")); lines.add("print("foobar");"); Files.write(Paths.get("res/nashorn1-modified.js"), lines);
要注意這些方法對(duì)內(nèi)存并不十分高效,因?yàn)檎麄€(gè)文件都會(huì)讀進(jìn)內(nèi)存。文件越大,所用的堆區(qū)也就越大。
你可以使用Files.lines方法來(lái)作為內(nèi)存高效的替代。這個(gè)方法讀取每一行,并使用函數(shù)式數(shù)據(jù)流來(lái)對(duì)其流式處理,而不是一次性把所有行都讀進(jìn)內(nèi)存。
try (Streamstream = Files.lines(Paths.get("res/nashorn1.js"))) { stream .filter(line -> line.contains("print")) .map(String::trim) .forEach(System.out::println); }
如果你需要更多的精細(xì)控制,你需要構(gòu)造一個(gè)新的BufferedReader來(lái)代替:
Path path = Paths.get("res/nashorn1.js"); try (BufferedReader reader = Files.newBufferedReader(path)) { System.out.println(reader.readLine()); }
或者,你需要寫入文件時(shí),簡(jiǎn)單地構(gòu)造一個(gè)BufferedWriter來(lái)代替:
Path path = Paths.get("res/output.js"); try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.write("print("Hello World");"); }
BufferedReader也可以訪問(wèn)函數(shù)式數(shù)據(jù)流。lines方法在它所有行上面構(gòu)建數(shù)據(jù)流:
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提供了三個(gè)簡(jiǎn)單的方法來(lái)讀取文本文件的每一行,使文件處理更加便捷。
不幸的是你需要顯式使用try-with語(yǔ)句來(lái)關(guān)閉文件流,這會(huì)使示例代碼有些凌亂。我期待函數(shù)式數(shù)據(jù)流可以在調(diào)用類似count和collect時(shí)可以自動(dòng)關(guān)閉,因?yàn)槟悴荒茉谙嗤瑪?shù)據(jù)流上調(diào)用終止操作兩次。
我希望你能喜歡這篇文章。所有示例代碼都托管在Github上,還有來(lái)源于我博客其它Java8文章的大量的代碼片段。如果這篇文章對(duì)你有所幫助,請(qǐng)收藏我的倉(cāng)庫(kù),并且在Twitter上關(guān)注我。
請(qǐng)堅(jiān)持編程!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64960.html
摘要:基本數(shù)據(jù)類型布爾類型或,占用位。強(qiáng)制類型轉(zhuǎn)換強(qiáng)制類型轉(zhuǎn)換的語(yǔ)法格式是,強(qiáng)制類型轉(zhuǎn)換的運(yùn)算符是圓括號(hào)。無(wú)符號(hào)右移運(yùn)算符。對(duì)于低于類型如和的操作數(shù)總是先自動(dòng)類型轉(zhuǎn)換為類型后再移位。 注釋 單行注釋//......多行注釋/ .../文檔注釋/*.../ 文檔注釋位于注釋內(nèi)容上一行 標(biāo)識(shí)符和關(guān)鍵字 分隔符 分號(hào):作為語(yǔ)句的分隔,每個(gè)Java語(yǔ)句必須使用分號(hào)作為結(jié)尾?;ɡㄌ?hào):定義一個(gè)代碼...
摘要:浮點(diǎn)數(shù)運(yùn)算余數(shù)運(yùn)算符還可以用于浮點(diǎn)數(shù)的運(yùn)算。這些語(yǔ)言中的冪運(yùn)算符有著比其他的單目運(yùn)算符如一元或一元更高的優(yōu)先級(jí)。但是作為例外,在中,運(yùn)算符被設(shè)計(jì)為比單目運(yùn)算符優(yōu)先級(jí)更低。 一、算術(shù)運(yùn)算符 運(yùn)算符 描述 例子 x 運(yùn)算結(jié)果 y 運(yùn)算結(jié)果 + 加法 x=y+2 7 5 - 減法 x=y-2 3 5 * 乘法 x=y*2 10 5 / 除法 x=y/2 2.5 5 ...
摘要:每個(gè)構(gòu)造函數(shù)定義了一類對(duì)象,表示由構(gòu)造函數(shù)初始化對(duì)象的集合。嚴(yán)格模式下,明確禁止八進(jìn)制數(shù)。日期和時(shí)間構(gòu)造函數(shù)用來(lái)創(chuàng)建表示日期和時(shí)間的對(duì)象,包含方法。模式匹配函數(shù)是一個(gè)構(gòu)造函數(shù),創(chuàng)建正則表達(dá)式。布爾值表示兩種狀態(tài),使用保留字和。 《Javascript權(quán)威指南》就是前端工程師口中常說(shuō)的犀牛書,得名是因?yàn)橹形姆g出版的書籍封面是一只犀牛,是學(xué)習(xí)JavaScript的必讀書籍。 JavaSc...
摘要:到目前為止,使用越來(lái)越廣泛,不光光只是它強(qiáng)大的生成技術(shù),而且它能夠與進(jìn)行很好的集成。注意使用數(shù)字范圍來(lái)定義集合時(shí)無(wú)需使用方括號(hào)數(shù)字范圍也支持反遞增的數(shù)字范圍如對(duì)象對(duì)象使用花括號(hào)包括中的對(duì)之間以英文冒號(hào)分隔,多組對(duì)之間以英文逗號(hào)分隔。 Freemarker的介紹 ??Freemarker 是一款模板引擎,是一種基于模版生成靜態(tài)文件的通用 工具,它是為程序員提供的一個(gè)開發(fā)包,或者說(shuō)是一個(gè)類...
摘要:剛開始學(xué)習(xí)的時(shí)候,可能是一頭霧水,不知道從何學(xué)起。這個(gè)教程總體來(lái)說(shuō)就像列文虎克教學(xué),細(xì)到極致,妙到毫巔。適合絕對(duì)零基礎(chǔ)的,每個(gè)知識(shí)點(diǎn)掰開了揉碎了講解。 剛開始學(xué)習(xí)java的時(shí)候,可能是一頭霧水,不知道從何學(xué)起。還有很多Java小白,在剛自學(xué)Java的時(shí)候玩命的學(xué)習(xí),玩命的記住Java原理,天天早上五點(diǎn)起床背Java的一些英...
閱讀 1275·2021-09-27 13:35
閱讀 2575·2021-09-06 15:12
閱讀 3392·2019-08-30 15:55
閱讀 2841·2019-08-30 15:43
閱讀 442·2019-08-29 16:42
閱讀 3454·2019-08-29 15:39
閱讀 3073·2019-08-29 12:28
閱讀 1251·2019-08-29 11:11