保時捷公佈電動汽車研發計畫 2020年前上市

據外媒報導,保時捷近日在北美車展上推出了2017年款保時捷911 Turbo和Turbo S,借著新車發佈的轟動效應,保時捷911專案主管伊哈德-莫斯勒(Erhard Mossle)宣佈,保時捷未來將在旗艦版跑車的基礎上推出插電混動版車型。

伊哈德-莫斯勒表示:“我們正在研究不同的解決方案,同時我們也在研發全電動汽車。當然,我們正在討論推出保時捷911插電版車型的可能性。眼下我們還有很多工作要做,比如如何在車內放置電池組等等。保時捷911插電版或在下一代車型中成為現實。”

伊哈德-莫斯勒還表示,插電版保時捷911跑車在研發過程中仍然會將性能放在首要考慮的位置,未來該款車很有可能保留6缸引擎,不過車輛也會兼顧滿足消費者對於更為綠色環保城市汽車的需求。

下一代保時捷911跑車預計和E任務轎車有望在2020年前同公眾見面。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置,台北網頁設計,各種案例分享

南極洲溫度創新高 攝氏18.2度跟台北一樣暖

摘錄自2020年2月9日聯合新聞網報導

紐約時報報導,世界上最冷、風勢最強也最乾燥的大陸南極洲,6日出現有紀錄以來最高溫,阿根廷在南極半島北端的艾斯伯蘭薩研究站測到攝氏18.2度高溫,打破2015年3月24日的最高溫紀錄攝氏17.5度,凸顯全球暖化的趨勢。

艾斯伯蘭薩目前處於夏季,6日出現攝氏18.2度,根據美國國家氣象局的資料,與洛杉磯當天出現的攝氏18度相當。根據中央氣象局的資料,這與位於中央氣象局內的台北測站當天上午8點到9點之間的溫度相當,6日當天上午8點是17.4度,9點是19.5度。

艾斯伯蘭薩從1961年起記錄氣溫。根據世界氣象組織的資料,南極洲的溫度通常不會這麼高,海岸地帶約是攝氏零下10度,內陸海拔較高處通常是攝氏零下60度。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

新德里市議員選舉投票 選民關心空污電費減免

摘錄自2020年02月09日中央通訊社印度報導

新德里今(8日)舉行市議員選舉投票,雖有政黨在選舉中主打國家安全政策,不過,許多選民更關心政府如何防治空污、提供社會保障、電費減免和照顧窮人等切身議題。

新德里超過1470萬名合格選民8日上午8時起到晚間6時,在1萬3751個投票所進行投票,從672名市議員候選人中選出70席;選舉結果將在11日出爐,進而由議會多數組成新一屆首都內閣。

小老百姓黨(AAP)上次選舉贏得67席市議員的多數在首都組閣執政,新德里市長克里瓦爾(Arvind Kejriwal)這次率領AAP尋求連任,並利用執政優勢陸續推出水電免費及無線網路、健康、教育免費及婦女免費搭乘公共交通工具等政策,希望吸引選民再給他們5年的執政機會。

不過,由印度總理莫迪和印度人民黨主席(BJP)兼內政部長夏哈(Amit Shah)率領的印度人民黨,從1993年以後就從未曾在新德里市議會選舉中拿下多數,這次希望挾著去年在國會選舉大勝的餘威,奪下首都執政權。

另外,印度人民黨也指控AAP在新德里無法提供乾淨的飲用水,而且沒有能力提供免費無線網路及安裝閉路電視。

不過,在南德里一處小學的投票所,一些排隊投票的市民大多關切的還是如何防治空氣污染、社會保障與社區安全、電力供應與電費減免等切身問題。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置,台北網頁設計,各種案例分享

寒潮來襲,電動車撐不住?材料改良或為解方

北極震盪下,中國、台灣面臨數十年難得一見的低溫,連深圳與台北的平地都下起冰霰。這樣的天氣暴露出電動車的一大缺點──由於鋰電池在低溫環境下放電量不穩,因此電動車的續航力也大受挑戰。

根據美國AAA Automotive Research Center所做的研究,同一輛電動車在攝氏24度左右的溫度下可行駛約168公里,但在攝氏-6.7度左右時,行駛距離大幅縮減到只剩約69公里。而美國能源部及環保署的資料也載示,同樣在攝氏-6.7度的環境下,汽油車續航力會下降12%,但電動車會降低34%。

EnergyTrend鋰電池/電動車研究指出,同樣的問題也會發生在手機、平板等攜帶裝置上,主要的原因是鋰電池長時間處於寒冷環境下時,因化學反應不正常,會使放電電流變少,導致可用容量降低。若是攜帶式裝置,可將裝置帶在身上「保暖」,但電動車可不能如法炮製。

電動車近兩年來展望長紅,不只有Tesla、Faraday Future等新公司出現,保時捷、BMW、Audi、Nissan、Toyota、Ford、福斯汽車等國際車廠也紛紛推出電動車款,且銷售量持續攀升。尤其在霾害嚴重的中國,已正式將電動車列為重點發展項目。但在氣候的限制下,如何解決鋰電池的續航力降低問題,就成為一大議題。

從材料面、系統面解決

EnergyTrend調研團隊指出,中國雖然大力推動電動車,但仍以華中以南地區為推行主力,原因正在於東北、華北等地氣候太過嚴寒,不利於鋰電池與電動車行駛,不只電池容量會降低,汽車也會有無法發動的問題。

要解決此一問題,主要分成材料改良與系統改良。在材料方面,可改用更耐嚴寒氣候的電池材料;在系統方面,則又分成耐低溫零件以及低溫系統的使用。分析師指出,目前已有廠商針對電動車耐寒的議題提出相關技術。

以台灣電動機車Gogoro為例,Gogoro設計有高溫預防模式與低溫保護模式,在低溫狀況下,低溫保護模式啟用並使動力輸出降低,但騎乘一段時間後電池溫度就能慢慢回到正常。

目前相關技術仍在發展當中,但隨著電動車市場擴大,這將成為不得不跨越的一大難題。

(照片來源:Wikepedia)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

怎麼把CAT客戶端的RootMessageId記錄到每條日誌中?

什麼是RootMessageId?

為了理解RootMessageId先簡單介紹一下CAT的數據結構設計。CAT客戶端會將所有消息都封裝為一個完整的消息樹(MessageTree),消息樹可能包括Transaction、Event、Heartbeat、Metric等類型的消息。具體如下:

  • Transaction:適合記錄跨越系統邊界的程序訪問行為,比如遠程調用,數據庫調用,也適合執行時間較長的業務邏輯監控,Transaction用來記錄一段代碼的執行時間和次數
  • Event:用來記錄一件事發生的次數,比如記錄系統異常,它和transaction相比缺少了時間的統計,開銷比transaction要小
  • Heartbeat:表示程序內定期產生的統計信息, 如CPU利用率, 內存利用率, 連接池狀態, 系統負載等
  • Metric:用於記錄業務指標、指標可能包含對一個指標記錄次數、記錄平均值、記錄總和,業務指標最低統計粒度為1分鐘

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

其中,Transaction類型的消息可作為消息樹節點,而其他消息只可作為消息樹的恭弘=叶 恭弘子節點,也就是Transaction是一個可嵌套的遞歸結構。比如:

消息樹的每一節點都有一個屬性messageId,用來唯一表示節點本身,其構成為:{domain}-{ip}-{timestamp}-{自增index}。另外還有兩個屬性,分別是parentMessageId, rootMessageId。parentMessageId表示父節點的messageId;rootMessageId則表示整個消息樹的根節點的messageId。這兩個屬性在之後CAT的調用鏈分析與分佈式調用鏈分析中發揮了關鍵作用。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

為什麼在日誌中記錄?

根據RootMessageId可以追蹤某一個請求的整個分佈式調用鏈,結合每一條日誌快速定位耗費性能的癥結,做針對性的性能優化。更加方便地做性能優化,特別是TP95、TP99等指標。

遇到偶爾發生的bug,是最讓人頭疼的,只有先從日誌中找線索,但是在海量的日誌中找到出現bug的那一個請求是很困難的。有了上游API提供的RootMessageId,就可以快速過濾出那次請求的所有日誌, 更快速更方便地定位線上bug。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

在日誌的什麼地方記錄?

當然是每一句日誌上都記錄RootMessageId了。有的同學會說,這日誌也記錄的太多了。當發現線上問題無法定位時,你就會狠日誌太少了。其實記錄日誌不怕多,就怕不全。現在硬盤很便宜了,搞個幾T沒有問題,另外還可以設置日誌清理策略。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

怎麼記錄到日誌中?

前面說了那麼多,終於到了今天的壓軸大戲了。實現記錄到日誌有很多種方式,這裏使用的是MDC(Mapped Diagnostic Contexts)。顧名思義,其目的是為了便於我們診斷線上問題而出現的方法工具類,目前我們經常使用的logback和log4j都是支持的。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

只需要在每個請求的入口調用MDC.put方法,把rootMessageId賦值進去就可以了,是不是很簡單?示例代碼:

//在Filter里,從header里獲取上下文信息,包括messageId、parentMessageId、rootMessageId
CatContext catContext = new CatContext();
catContext.addProperty(Cat.Context.ROOT, request.getHeader(CatConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID));
catContext.addProperty(Cat.Context.PARENT, request.getHeader(CatConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID));
catContext.addProperty(Cat.Context.CHILD, request.getHeader(CatConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID));
if (catContext.getProperty(Cat.Context.ROOT) == null) {
    //如果調用鏈的頂端,沒有上下文信息,需要生成上下文信息
    Cat.logRemoteCallClient(catContext);
} else {
    Cat.logRemoteCallServer(catContext);
}
MDC.put("traceId", catContext.getProperty(Cat.Context.ROOT));

如果你還不知道怎麼集成CAT調用鏈,可以看看之前的《》

然後,在設置日誌輸出格式的配置文件里增加[%X{traceId}]。

Logback的xml配置示例:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true">
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%X{traceId}] [%-5level] [%-40.36logger{40}:%-4.4line] - %msg%n</pattern>
        </layout>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

log4j的properties配置示例:

log4j.rootCategory=INFO,stdout,info,error
log4j.rootLooger=warn,stdout,info,error
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%-5p] [%thread] [%X{traceId}]  method:%l - %m%n

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

Java升級那麼快,多個版本如何靈活切換和管理?

前言

近兩年,Java 版本升級頻繁,感覺剛剛掌握 Java8,寫本文時,已聽到 java14 的消息,無論是嘗鮮新特性(),還是由於項目升級/兼容需要,我們可能都要面臨管理多個 Java 版本的情

另外 Oracle 自 Java11 開始,更改了用戶協議,任何商用都會收費。在寫本文時,得到消息「微軟宣布加入 OpenJDK」,打不過就選擇 OpenJDK。隨便 G 一下,當個故事了解就可以

配置單個 Java 環境變量本身沒什麼技術含量可言,但當需要管理多個 Java 版本,重複配置環境變量顯然是非常枯燥的,按照傳統的配置方式我們又不能靈活的切換 Java 版本

那要如何輕鬆管理與使用多個版本 Java?

多版本 Java 管理

顯然我們不是第一個有這種困境的人,我所知道的現有方案有三種:

本文主要說明如何通過 sdkman 打破我們面臨的困境,幫助我們靈活配置與使用 Java

sdkman 介紹

SDKMAN 是一個用於在大多數基於 Unix 系統上管理 多個軟件開發工具包 (Java, Groovy, Scala, Kotlin and Ceylon. Ant, Gradle 等) 的并行版本的工具 。

它提供了一個方便的命令行接口 (CLI) 和 API,用於安裝、切換、刪除和列出候選對象。本文主要通過管理 Java 來說明 sdkman 的使用

sdkman 安裝

在類 unix 平台上安裝 sdkman 非常容易。它可以順利的安裝在Mac OSX、Linux、WLS、Cygwin、Solaris和FreeBSD 上,同時還支持Bash和 ZSH shell。

只需打開一個新終端機並輸入:

$ curl -s "https://get.sdkman.io" | bash

按照相應的指令提示,完成相應的操作後繼續輸入:

$ source "$HOME/.sdkman/bin/sdkman-init.sh"

到這裏我們就可以驗證 sdk 的安裝版本了:

$ sdk version

上圖紅色框標記显示我當前 sdkman 的版本,每次執行 sdk version 命令時,都會檢查是否會有新版本,如果要更新輸入 y 就可以

有些系統發行版本不包含 zip 和 unzip,如果安裝時遇到相關錯誤,可以輸入如下命令安裝 zip 和 unzip

$ sudo apt-get install zip unzip

從上面的安裝命令上可以看出,sdkman 默認的安裝路徑是在$HOME/.sdkman 下,我們也可以自定義安裝路徑,只需要指定 SDKMAN_DIR 變量值就好了:

$ export SDKMAN_DIR="/usr/local/sdkman" && curl -s "https://get.sdkman.io" | bash

到這裏 sdkman 的安裝就結束了,我們來看看如何使用

sdkman 使用教程

命令行下學習一個新玩意當然是查看它的 help 命令,輸入:

$ sdkman help

感覺上圖按顏色區分內容后,sdkman 的使用說明也就結束了,我們按照上面的圖來詳細說明一下使用教程

sdk list

先來輸入:

$ sdk list

綠色的標記就是 sdkman 集成的所有可用的 candidate,通過按回車「enter」按鍵,會看到更多可用 candidate

我們指定 candidate,輸入:

$ sdk list java

從上圖中可以看到所有 java 可用的版本 version,以及標識 indentifier,以及狀態 status,我已經安裝了 java 12 和 11

有了這些信息做鋪墊,我們可以安裝任意 sdkman 內置的軟件開發包了,繼續以 java 為例

sdk install

回看 sdkman help 命令的輸出,使用 install 命令,我們再安裝一個 Java 最新 13.0.1.j9 版本

從上圖你可以看出,綠色標記的內容是 list 命令結果中的 version 值,但是報錯不可用,輸入indentifier 編號才能正常下載,這裏需要注意

安裝完后,status 就會編程 installed 狀態

sdk current

當安裝多個版本的 java 時,我們輸入下面命令獲取當前正在用 candidate 的版本

$ sdk current java

sdk use

了解了當前使用版本,如果我們想切換到其他版本, 可以輸入:

$ sdk use java 12.0.2.j9-adpt

注意️: 這裏同樣是指定的 indentifier 的值

sdk default

如果我們想指定某個版本為默認版本,可以輸入:

$ sdk default java jdk1.8.0_162.jdk

注意️: 這裏同樣是指定的 indentifier 的值

sdk uninstall

當我們想卸載某個版本可以輸入:

$ sdk uninstall java 12.0.2.j9-adpt

注意️: 這裏同樣是指定的 indentifier 的值

sdk upgrade

如果我們想升級某個 candidate,可以輸入:

$ sdk upgrade java

sdk flush

使用 sdkman 時間變長也會慢慢產生很多緩存內容,我們可以輸入
清理廣播消息:

$ sdk flush broadcast

清理下載的 sdk 二進制文件(長時間使用后清理,可以節省出很多空間):

$ sdk flush archives

清理臨時文件內容:

$ sdk flush temp

到這裏 sdkman 的基本使用就已經介紹完了,其實這些命令都不用急,想不起來的時候執行 sdk help 來臨時查看一下就好

sdkman 卸載

如果我們不喜歡 sdkman 了,我們也可以輕鬆的卸載掉它:

$ tar zcvf ~/sdkman-backup_$(date +%F-%kh%M).tar.gz -C ~/ .sdkman
$ rm -rf ~/.sdkman

最後打開你的 .bashrc、.bash_profile 和/或者 .profile,找到並刪除下面這幾行。

#THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!!
[[ -s "/home/dudette/.sdkman/bin/sdkman-init.sh" ]] && source "/home/dudette/.sdkman/bin/sdkman-init.sh"

我用的 zshrc,找到 .zshrc 文件刪除掉上面內容即可

到這裏基於 Unix 系統的,有關 sdkman 的安裝,使用及下載都已經介紹完了,可以上手試一試了,相信很多小夥伴用的是 Windows,除了 jenv, sdkman 和 Jabba 都有 windows 用戶的解決方案:

sdkman windows 解決方案

在 sdkman 官網首頁同樣為 windows 用戶提供了解決方案,小夥伴們找到如下位置查看即可

我看了一下過程,也是很簡單,由於手頭沒有 windows 電腦,這個請小夥伴們自行嘗試吧,有問題歡迎留言

靈魂追問

  1. 你現在用的 Java 版本是多少?
  2. 有跟隨新版本嘗試更多新特性嗎?
  3. 你的燈還亮着嗎?

    歡迎持續關注公眾號:「日拱一兵」

    • 前沿 Java 技術乾貨分享
    • 高效工具匯總 | 回復「工具」
    • 面試問題分析與解答
    • 技術資料領取 | 回復「資料」

以讀偵探小說思維輕鬆趣味學習 Java 技術棧相關知識,本着將複雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注……

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※南投搬家前需注意的眉眉角角,別等搬了再說!

[視頻演示].NET Core開發的iNeuOS物聯網平台,實現從設備&PLC、雲平台、移動APP數據鏈路閉環

目       錄

1.      概述… 1

2.      登陸信息… 2

3.      設備驅動… 3

4.      組態建模… 3

5.      手機APP. 5

6.      視頻演示… 6

1.   概述

此次我們團隊人員對iNeuOS進行了全面升級,主要升級內容包括:

(1)    設備容器增加設備驅動,包括:西門子(S7-200smart、S7-300、S7-400、S7-1200、S7-1500)、三菱(FxSerial)、MQTT協議等。

(2)    組態建模可以設置背景,作為開發大屏展示使用,背景可以為一個獨立的圖元信息。

(3)    組態建模可以按瀏覽器大小按比例縮放,以適應移動APP显示。

(4)    組態建模標題框和文本框可以設置背景和字體顏色。

(5)    組態建模文本框綁定數據點后,可以設置上下限值,以用於判斷,進行顏色報警显示。

(6)    組態建模文本框可以設置數據保留小數點位數。

(7)    組態建模可以自定義畫任意圖形,並且填充顏色等。

(8)    組態建模開發好的視圖,可以右鍵單擊數值文本框,查看數據曲線趨勢。

(9)    手機移動APP,組態建模開發的視圖,直接可以显示在手機APP上。

(10)進行其他優化。

2.   演示信息

在線演示:  (注:服務器比較慢,請耐心等待。用戶自已註冊用戶,體驗系統功能)

視頻演示:

驅動開發: (v2.1版本)

手機APP:

登陸界面,如下圖:

3.   設備驅動

   主要了主流的PLC驅動,包括:西門子(S7-200smart、S7-300、S7-400、S7-1200、S7-1500)、三菱(FxSerial)。增加了MQTT協議,MQTT協議基本與阿里雲IOT的alink協議保持一致,方便在私有雲建設過程中遷移。如下圖:

      iNeuKernel現在完全支持跨平台部署,至此iNeuOS的前台和後台全部支持跨平台。

4.   組態建模

   雲端組態建模改動和完善的地方比較多,主要依據用戶提出的建議進行了修改。

(1)增加編輯區域設置背景功能,主要用於開發數據大屏展示的效果,可以上傳多個背景圖元,並且作為一個獨立的圖元,隨時可以更換背景。

  (2)編輯數據的文本框,可以設置背景和字體顏色、報警運作、保留小數位數等,更具有交互感。

  (3)任意畫圖形,儘管我們提供了3500多個SVG圖元信息,但是難免缺少特殊領域的圖形,可以使用這個功能彌補。也可以畫大屏展示的特定區域形式。

  (4)組態建模開發完成后,可以右鍵單擊文本框,查看趨勢,以展示當前數據的曲線圖。

5.   手機APP

    手機APP可以使數據離用戶最近,組態建模完成后,通過手機進行縮放显示。

      打開手機APP显示已經開發完成的視圖列表,選擇任意視圖,显示效果如下圖。

6.   視頻演示


如果你播放不了該視頻, 那是你的設備不支持該文件格式

參見:

《》

《》

《》

 物聯網&集成技術 QQ群:54256083 

 物聯網&集成合作 QQ群:727664080

 網站:

聯繫QQ:504547114

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

還看不懂同事的代碼?超強的 Stream 流操作姿勢還不學習一下

Java 8 新特性系列文章索引。

前言

我們都知道 Lambda 和 Stream 是 Java 8 的兩大亮點功能,在前面的文章里已經介紹過 Lambda 相關知識,這次介紹下 Java 8 的 Stream 流操作。它完全不同於 java.io 包的 Input/Output Stream ,也不是大數據實時處理的 Stream 流。這個 Stream 流操作是 Java 8 對集合操作功能的增強,專註於對集合的各種高效、便利、優雅的聚合操作。藉助於 Lambda 表達式,顯著的提高編程效率和可讀性。且 Stream 提供了并行計算模式,可以簡潔的編寫出并行代碼,能充分發揮如今計算機的多核處理優勢。

在使用 Stream 流操作之前你應該先了解 Lambda 相關知識,如果還不了解,可以參考之前文章: 。

1. Stream 流介紹

Stream 不同於其他集合框架,它也不是某種數據結構,也不會保存數據,但是它負責相關計算,使用起來更像一個高級的迭代器。在之前的迭代器中,我們只能先遍歷然後在執行業務操作,而現在只需要指定執行什麼操作, Stream 就會隱式的遍歷然後做出想要的操作。另外 Stream 和迭代器一樣的只能單向處理,如同奔騰長江之水一去而不復返。

由於 Stream 流提供了惰性計算和并行處理的能力,在使用并行計算方式時數據會被自動分解成多段然後并行處理,最後將結果匯總。所以 Stream 操作可以讓程序運行變得更加高效。

2. Stream 流概念

Stream 流的使用總是按照一定的步驟進行,可以抽象出下面的使用流程。

數據源(source) -> 數據處理/轉換(intermedia) -> 結果處理(terminal )

2.1. 數據源

數據源(source)也就是數據的來源,可以通過多種方式獲得 Stream 數據源,下面列舉幾種常見的獲取方式。

  • Collection.stream(); 從集合獲取流。
  • Collection.parallelStream(); 從集合獲取并行流。
  • Arrays.stream(T array) or Stream.of(); 從數組獲取流。
  • BufferedReader.lines(); 從輸入流中獲取流。
  • IntStream.of() ; 從靜態方法中獲取流。
  • Stream.generate(); 自己生成流

2.2. 數據處理

數據處理/轉換(intermedia)步驟可以有多個操作,這步也被稱為intermedia(中間操作)。在這個步驟中不管怎樣操作,它返回的都是一個新的流對象,原始數據不會發生任何改變,而且這個步驟是惰性計算處理的,也就是說只調用方法並不會開始處理,只有在真正的開始收集結果時,中間操作才會生效,而且如果遍歷沒有完成,想要的結果已經獲取到了(比如獲取第一個值),會停止遍歷,然後返回結果。惰性計算可以顯著提高運行效率。

數據處理演示。

@Test
public void streamDemo(){
    List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
    // 1. 篩選出名字長度為4的
    // 2. 名字前面拼接 This is
    // 3. 遍歷輸出
    nameList.stream()
            .filter(name -> name.length() == 4)
            .map(name -> "This is "+name)
            .forEach(name -> System.out.println(name));
}
// 輸出結果
// This is Jack
// This is Poul

數據處理/轉換操作自然不止是上面演示的過濾 filter 和 map映射兩種,另外還有 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等。

2.3. 收集結果

結果處理(terminal )是流處理的最後一步,執行完這一步之後流會被徹底用盡,流也不能繼續操作了。也只有到了這個操作的時候,流的數據處理/轉換等中間過程才會開始計算,也就是上面所說的惰性計算。結果處理也必定是流操作的最後一步。

常見的結果處理操作有 forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator 等。

下面演示了簡單的結果處理的例子。

/**
 * 轉換成為大寫然後收集結果,遍歷輸出
 */
@Test
public void toUpperCaseDemo() {
    List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
    List<String> upperCaseNameList = nameList.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
    upperCaseNameList.forEach(name -> System.out.println(name + ","));
}
// 輸出結果
// DARCY,CHRIS,LINDA,SID,KIM,JACK,POUL,PETER,

2.4. short-circuiting

有一種 Stream 操作被稱作 short-circuiting ,它是指當 Stream 流無限大但是需要返回的 Stream 流是有限的時候,而又希望它能在有限的時間內計算出結果,那麼這個操作就被稱為short-circuiting。例如 findFirst 操作。

3. Stream 流使用

Stream 流在使用時候總是藉助於 Lambda 表達式進行操作,Stream 流的操作也有很多種方式,下面列舉的是常用的 11 種操作。

3.1. Stream 流獲取

獲取 Stream 的幾種方式在上面的 Stream 數據源里已經介紹過了,下面是針對上面介紹的幾種獲取 Stream 流的使用示例。

@Test
public void createStream() throws FileNotFoundException {
    List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
    String[] nameArr = {"Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter"};
    // 集合獲取 Stream 流
    Stream<String> nameListStream = nameList.stream();
    // 集合獲取并行 Stream 流
    Stream<String> nameListStream2 = nameList.parallelStream();
    // 數組獲取 Stream 流
    Stream<String> nameArrStream = Stream.of(nameArr);
    // 數組獲取 Stream 流
    Stream<String> nameArrStream1 = Arrays.stream(nameArr);
    // 文件流獲取 Stream 流
    BufferedReader bufferedReader = new BufferedReader(new FileReader("README.md"));
    Stream<String> linesStream = bufferedReader.lines();
    // 從靜態方法獲取流操作
    IntStream rangeStream = IntStream.range(1, 10);
    rangeStream.limit(10).forEach(num -> System.out.print(num+","));
    System.out.println();
    IntStream intStream = IntStream.of(1, 2, 3, 3, 4);
    intStream.forEach(num -> System.out.print(num+","));
}

3.2. forEach

forEach 是 Strean 流中的一個重要方法,用於遍歷 Stream 流,它支持傳入一個標準的 Lambda 表達式。但是它的遍歷不能通過 return/break 進行終止。同時它也是一個 terminal 操作,執行之後 Stream 流中的數據會被消費掉。

如輸出對象。

List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numberList.stream().forEach(number -> System.out.println(number+","));
// 輸出結果
// 1,2,3,4,5,6,7,8,9,

3.3. map / flatMap

使用 map 把對象一對一映射成另一種對象或者形式。

/**
 * 把数字值乘以2
 */
@Test
public void mapTest() {
    List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    // 映射成 2倍数字
    List<Integer> collect = numberList.stream()
            .map(number -> number * 2)
            .collect(Collectors.toList());
    collect.forEach(number -> System.out.print(number + ","));
    System.out.println();

    numberList.stream()
            .map(number -> "数字 " + number + ",")
            .forEach(number -> System.out.println(number));
}
// 輸出結果
// 2,4,6,8,10,12,14,16,18,
// 数字 1,数字 2,数字 3,数字 4,数字 5,数字 6,数字 7,数字 8,数字 9,

上面的 map 可以把數據進行一對一的映射,而有些時候關係可能不止 1對 1那麼簡單,可能會有1對多。這時可以使用 flatMap。下面演示使用 flatMap把對象扁平化展開。

/**
 * flatmap把對象扁平化
 */
@Test
public void flatMapTest() {
    Stream<List<Integer>> inputStream = Stream.of(
            Arrays.asList(1),
            Arrays.asList(2, 3),
            Arrays.asList(4, 5, 6)
    );
    List<Integer> collect = inputStream
            .flatMap((childList) -> childList.stream())
            .collect(Collectors.toList());
    collect.forEach(number -> System.out.print(number + ","));
}
// 輸出結果
// 1,2,3,4,5,6,

3.4. filter

使用 filter 進行數據篩選,挑選出想要的元素,下面的例子演示怎麼挑選出偶數数字。

/**
 * filter 數據篩選
 * 篩選出偶數数字
 */
@Test
public void filterTest() {
    List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    List<Integer> collect = numberList.stream()
            .filter(number -> number % 2 == 0)
            .collect(Collectors.toList());
    collect.forEach(number -> System.out.print(number + ","));
}

得到如下結果。

2,4,6,8,

3.5. findFirst

findFirst 可以查找出 Stream 流中的第一個元素,它返回的是一個 Optional 類型,如果還不知道 Optional 類的用處,可以參考之前文章 。

/**
 * 查找第一個數據
 * 返回的是一個 Optional 對象
 */
@Test
public void findFirstTest(){
    List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    Optional<Integer> firstNumber = numberList.stream()
            .findFirst();
    System.out.println(firstNumber.orElse(-1));
}
// 輸出結果
// 1

findFirst 方法在查找到需要的數據之後就會返回不再遍歷數據了,也因此 findFirst 方法可以對有無限數據的 Stream 流進行操作,也可以說 findFirst 是一個 short-circuiting 操作。

3.6. collect / toArray

Stream 流可以輕鬆的轉換為其他結構,下面是幾種常見的示例。

 /**
 * Stream 轉換為其他數據結構
 */
@Test
public void collectTest() {
    List<Integer> numberList = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5);
    // to array
    Integer[] toArray = numberList.stream()
            .toArray(Integer[]::new);
    // to List
    List<Integer> integerList = numberList.stream()
            .collect(Collectors.toList());
    // to set
    Set<Integer> integerSet = numberList.stream()
            .collect(Collectors.toSet());
    System.out.println(integerSet);
    // to string
    String toString = numberList.stream()
            .map(number -> String.valueOf(number))
            .collect(Collectors.joining()).toString();
    System.out.println(toString);
    // to string split by ,
    String toStringbJoin = numberList.stream()
            .map(number -> String.valueOf(number))
            .collect(Collectors.joining(",")).toString();
    System.out.println(toStringbJoin);
}
// 輸出結果
// [1, 2, 3, 4, 5]
// 112233445
// 1,1,2,2,3,3,4,4,5

3.7. limit / skip

獲取或者扔掉前 n 個元素

/**
 * 獲取 / 扔掉前 n 個元素
 */
@Test
public void limitOrSkipTest() {
    // 生成自己的隨機數流
    List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
    ageList.stream()
            .limit(3)
            .forEach(age -> System.out.print(age+","));
    System.out.println();
    
    ageList.stream()
            .skip(3)
            .forEach(age -> System.out.print(age+","));
}
// 輸出結果
// 11,22,13,
// 14,25,26,

3.8. Statistics

數學統計功能,求一組數組的最大值、最小值、個數、數據和、平均數等。

/**
 * 數學計算測試
 */
@Test
public void mathTest() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
    IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics();
    System.out.println("最小值:" + stats.getMin());
    System.out.println("最大值:" + stats.getMax());
    System.out.println("個數:" + stats.getCount());
    System.out.println("和:" + stats.getSum());
    System.out.println("平均數:" + stats.getAverage());
}
// 輸出結果
// 最小值:1
// 最大值:6
// 個數:6
// 和:21
// 平均數:3.5

3.9. groupingBy

分組聚合功能,和數據庫的 Group by 的功能一致。

/**
 * groupingBy
 * 按年齡分組
 */
@Test
public void groupByTest() {
    List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
    Map<String, List<Integer>> ageGrouyByMap = ageList.stream()            
        .collect(Collectors.groupingBy(age -> String.valueOf(age / 10)));
    ageGrouyByMap.forEach((k, v) -> {
        System.out.println("年齡" + k + "0多歲的有:" + v);
    });
}
// 輸出結果
// 年齡10多歲的有:[11, 13, 14]
// 年齡20多歲的有:[22, 25, 26]

3.10. partitioningBy

/**
 * partitioningBy
 * 按某個條件分組
 * 給一組年齡,分出成年人和未成年人
 */
public void partitioningByTest() {
    List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
    Map<Boolean, List<Integer>> ageMap = ageList.stream()
            .collect(Collectors.partitioningBy(age -> age > 18));
    System.out.println("未成年人:" + ageMap.get(false));
    System.out.println("成年人:" + ageMap.get(true));
}
// 輸出結果
// 未成年人:[11, 13, 14]
// 成年人:[22, 25, 26]

3.11. 進階 – 自己生成 Stream 流

/**
 * 生成自己的 Stream 流
 */
@Test
public void generateTest(){
    // 生成自己的隨機數流
    Random random = new Random();
    Stream<Integer> generateRandom = Stream.generate(random::nextInt);
    generateRandom.limit(5).forEach(System.out::println);
    // 生成自己的 UUID 流
    Stream<UUID> generate = Stream.generate(UUID::randomUUID);
    generate.limit(5).forEach(System.out::println);
}

// 輸出結果
// 793776932
// -2051545609
// -917435897
// 298077102
// -1626306315
// 31277974-841a-4ad0-a809-80ae105228bd
// f14918aa-2f94-4774-afcf-fba08250674c
// d86ccefe-1cd2-4eb4-bb0c-74858f2a7864
// 4905724b-1df5-48f4-9948-fa9c64c7e1c9
// 3af2a07f-0855-455f-a339-6e890e533ab3

上面的例子中 Stream 流是無限的,但是獲取到的結果是有限的,使用了 Limit 限制獲取的數量,所以這個操作也是 short-circuiting 操作。

4. Stream 流優點

4.1. 簡潔優雅

正確使用並且正確格式化的 Stream 流操作代碼不僅簡潔優雅,更讓人賞心悅目。下面對比下在使用 Stream 流和不使用 Stream 流時相同操作的編碼風格。

/**
 * 使用流操作和不使用流操作的編碼風格對比
 */
@Test
public void diffTest() {
    // 不使用流操作
    List<String> names = Arrays.asList("Jack", "Jill", "Nate", "Kara", "Kim", "Jullie", "Paul", "Peter");
    // 篩選出長度為4的名字
    List<String> subList = new ArrayList<>();
    for (String name : names) {
        if (name.length() == 4) {
            subList.add(name);
        }
    }
    // 把值用逗號分隔
    StringBuilder sbNames = new StringBuilder();
    for (int i = 0; i < subList.size() - 1; i++) {
        sbNames.append(subList.get(i));
        sbNames.append(", ");
    }
    // 去掉最後一個逗號
    if (subList.size() > 1) {
        sbNames.append(subList.get(subList.size() - 1));
    }
    System.out.println(sbNames);
}
// 輸出結果
// Jack, Jill, Nate, Kara, Paul

如果是使用 Stream 流操作。

// 使用 Stream 流操作
String nameString = names.stream()
       .filter(num -> num.length() == 4)
       .collect(Collectors.joining(", "));
System.out.println(nameString);

4.2. 惰性計算

上面有提到,數據處理/轉換(intermedia) 操作 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等這些操作,在調用方法時並不會立即調用,而是在真正使用的時候才會生效,這樣可以讓操作延遲到真正需要使用的時刻。

下面會舉個例子演示這一點。

 /**
  * 找出偶數
  */
 @Test
 public void lazyTest() {
     // 生成自己的隨機數流
     List<Integer> numberLIst = Arrays.asList(1, 2, 3, 4, 5, 6);
     // 找出偶數
     Stream<Integer> integerStream = numberLIst.stream()
             .filter(number -> {
                 int temp = number % 2;
                 if (temp == 0 ){
                     System.out.println(number);
                 }
                 return temp == 0;
             });

     System.out.println("分割線");
     List<Integer> collect = integerStream.collect(Collectors.toList());
 }

如果沒有 惰性計算,那麼很明顯會先輸出偶數,然後輸出 分割線。而實際的效果是。

分割線
2
4
6

可見 惰性計算 把計算延遲到了真正需要的時候。

4.3. 并行計算

獲取 Stream 流時可以使用 parallelStream 方法代替 stream 方法以獲取并行處理流,并行處理可以充分的發揮多核優勢,而且不增加編碼的複雜性。

下面的代碼演示了生成一千萬個隨機數后,把每個隨機數乘以2然後求和時,串行計算和并行計算的耗時差異。

  /**
  * 并行計算
  */
 @Test
 public void main() {
     // 生成自己的隨機數流,取一千萬個隨機數
     Random random = new Random();
     Stream<Integer> generateRandom = Stream.generate(random::nextInt);
     List<Integer> numberList = generateRandom.limit(10000000).collect(Collectors.toList());

     // 串行 - 把一千萬個隨機數,每個隨機數 * 2 ,然後求和
     long start = System.currentTimeMillis();
     int sum = numberList.stream()
         .map(number -> number * 2)
         .mapToInt(x -> x)
         .sum();
     long end = System.currentTimeMillis();
     System.out.println("串行耗時:"+(end - start)+"ms,和是:"+sum);

     // 并行 - 把一千萬個隨機數,每個隨機數 * 2 ,然後求和
     start = System.currentTimeMillis();
     sum = numberList.parallelStream()
         .map(number -> number * 2)
         .mapToInt(x -> x)
         .sum();
     end = System.currentTimeMillis();
     System.out.println("并行耗時:"+(end - start)+"ms,和是:"+sum);
 }

得到如下輸出。

串行耗時:1005ms,和是:481385106
并行耗時:47ms,和是:481385106

效果顯而易見,代碼簡潔優雅。

5. Stream 流建議

5.1 保證正確排版

從上面的使用案例中,可以發現使用 Stream 流操作的代碼非常簡潔,而且可讀性更高。但是如果不正確的排版,那麼看起來將會很糟糕,比如下面的同樣功能的代碼例子,多幾層操作呢,是不是有些讓人頭大?

// 不排版
String string = names.stream().filter(num -> num.length() == 4).map(name -> name.toUpperCase()).collect(Collectors.joining(","));
// 排版
String string = names.stream()
        .filter(num -> num.length() == 4)
        .map(name -> name.toUpperCase())
        .collect(Collectors.joining(","));

5.1 保證函數純度

如果想要你的 Stream 流對於每次的相同操作的結果都是相同的話,那麼你必須保證 Lambda 表達式的純度,也就是下面亮點。

  • Lambda 中不會更改任何元素。
  • Lambda 中不依賴於任何可能更改的元素。

這兩點對於保證函數的冪等非常重要,不然你程序執行結果可能會變得難以預測,就像下面的例子。

@Test
public void simpleTest(){
    List<Integer> numbers = Arrays.asList(1, 2, 3);
    int[] factor = new int[] { 2 };
    Stream<Integer> stream = numbers.stream()
            .map(e -> e * factor[0]);
    factor[0] = 0;
    stream.forEach(System.out::println);
}
// 輸出結果
// 0
// 0
// 0

文中代碼都已經上傳到

。

個人網站:
如果你喜歡這篇文章,可以關注公眾號,一起成長。
關注公眾號回復資源可以沒有套路的獲取全網最火的的 Java 核心知識整理&面試資料。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置,台北網頁設計,各種案例分享

2019能源碳排放不再增長 IEA:見證能源轉型是現在進行式

環境資訊中心綜合外電;彭瑞祥 編譯

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

暖冬導致「熊出沒」增加 日本9個月捕殺近5700隻熊創新高

摘錄自2020年2月19日星島日報報導

據日本共同社報道,2019年4至12月日本捕殺亞洲黑熊和棕熊的數目更創歷年新高,多達5,667隻,本年度尚未結束就已創下歷史新高。

據統計,日本自2007年度起,保持捕殺1,000至3,000多隻, 2018年度為3,446頭,但19年度尚未結束,已創下新高,較18年度增加39.1%。日本專家分析,可能與熊的分布地區擴大、食物減少有關。

報道又指,去年進入12月熊的冬眠期後,仍有逾200隻熊被捕殺,專家估計可能是因為天氣暖導致熊的冬眠期推遲,因而四處覓食。2019年度至12月底為止,「熊出沒」事故的受害人達155人,其中20人受傷。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置,台北網頁設計,各種案例分享