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