自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

開發(fā) 后端
前不久幫同事一起 review 一個(gè) job 執(zhí)行緩慢的問題時(shí)發(fā)現(xiàn)不少朋友在擼碼實(shí)現(xiàn)功能時(shí)還是有需要細(xì)節(jié)不夠注意,于是便有了這篇文章。

 前言

前不久幫同事一起 review 一個(gè) job 執(zhí)行緩慢的問題時(shí)發(fā)現(xiàn)不少朋友在擼碼實(shí)現(xiàn)功能時(shí)還是有需要細(xì)節(jié)不夠注意,于是便有了這篇文章。

[[313003]]

ArrayList 踩坑

  1. List<String> temp = new ArrayList() ;//獲取一批數(shù)據(jù)List<String> all = getData();for(String str : all) {    temp.add(str);} 

首先大家看看這段代碼有什么問題嘛?

其實(shí)在大部分情況下這都是沒啥問題,無非就是循環(huán)的往 ArrayList 中寫入數(shù)據(jù)而已。

但在特殊情況下,比如這里的 getData() 返回?cái)?shù)據(jù)非常巨大時(shí)后續(xù) temp.add(str) 就會(huì)有問題了。

比如我們?cè)?review 代碼時(shí)發(fā)現(xiàn)這里返回的數(shù)據(jù)有時(shí)會(huì)高達(dá) 2000W,這時(shí) ArrayList 寫入的問題就凸顯出來了。

填坑指南

大家都知道 ArrayList 是由數(shù)組實(shí)現(xiàn),而數(shù)據(jù)的長度有限;需要在合適的時(shí)機(jī)對(duì)數(shù)組擴(kuò)容。

這里以插入到尾部為例 add(E e)。

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑
  1. ArrayListtemp=newArrayList<>(2);temp.add("1");temp.add("2");temp.add("3"); 

當(dāng)我們初始化一個(gè)長度為 2 的 ArrayList ,并往里邊寫入三條數(shù)據(jù)時(shí) ArrayList 就得擴(kuò)容了,也就是將之前的數(shù)據(jù)復(fù)制一份到新的數(shù)組長度為 3 的數(shù)組中。

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

之所以是 3 ,是因?yàn)樾碌拈L度=原有長度 * 1.5

通過源碼我們可以得知 ArrayList 的默認(rèn)長度為 10.

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

但其實(shí)并不是在初始化的時(shí)候就創(chuàng)建了 DEFAULT_CAPACITY=10 的數(shù)組。

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

而是在往里邊 add 第一個(gè)數(shù)據(jù)的時(shí)候會(huì)擴(kuò)容到 10.

既然知道了默認(rèn)的長度為 10 ,那說明后續(xù)一旦寫入到第九個(gè)元素的時(shí)候就會(huì)擴(kuò)容為 10*1.5=15。這一步為數(shù)組復(fù)制,也就是要重新開辟一塊新的內(nèi)存空間存放這 15 個(gè)數(shù)組。

一旦我們頻繁且數(shù)量巨大的進(jìn)行寫入時(shí)就會(huì)導(dǎo)致許多的數(shù)組復(fù)制,這個(gè)效率是極低的。

但如果我們提前預(yù)知了可能會(huì)寫入多少條數(shù)據(jù)時(shí)就可以提前避免這個(gè)問題。

比如我們往里邊寫入 1000W 條數(shù)據(jù),在初始化的時(shí)候就給定數(shù)組長度與用默認(rèn) 10 的長度之間性能是差距巨大的。

我用 JMH 基準(zhǔn)測試驗(yàn)證如下:

  1. @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)public class CollectionsTest {    private static final int TEN_MILLION = 10000000;    @Benchmark    @BenchmarkMode(Mode.AverageTime)    @OutputTimeUnit(TimeUnit.MICROSECONDS)    public void arrayList() {        List<String> array = new ArrayList<>();        for (int i = 0; i < TEN_MILLION; i++) {            array.add("123");        }    }    @Benchmark    @BenchmarkMode(Mode.AverageTime)    @OutputTimeUnit(TimeUnit.MICROSECONDS)    public void arrayListSize() {        List<String> array = new ArrayList<>(TEN_MILLION);        for (int i = 0; i < TEN_MILLION; i++) {            array.add("123");        }    }    public static void main(String[] args) throws RunnerException {        Options opt = new OptionsBuilder()                .include(CollectionsTest.class.getSimpleName())                .forks(1)                .build();        new Runner(opt).run();    }} 

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

根據(jù)結(jié)果可以看出預(yù)設(shè)長度的效率會(huì)比用默認(rèn)的效率高上很多(這里的 Score 指執(zhí)行完函數(shù)所消耗的時(shí)間)。

所以這里強(qiáng)烈建議大家:在有大量數(shù)據(jù)寫入 ArrayList 時(shí),一定要初始化指定長度。

再一個(gè)是一定要慎用 add(intindex,E element) 向指定位置寫入數(shù)據(jù)。

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

通過源碼我們可以看出,每一次寫入都會(huì)將 index 后的數(shù)據(jù)往后移動(dòng)一遍,其實(shí)本質(zhì)也是要復(fù)制數(shù)組;

但區(qū)別于往常規(guī)的往數(shù)組尾部寫入數(shù)據(jù),它每次都會(huì)進(jìn)行數(shù)組復(fù)制,效率極低。

LinkedList

提到 ArrayList 就不得不聊下 LinkedList 這個(gè)孿生兄弟;雖說都是 List 的容器,但本質(zhì)實(shí)現(xiàn)卻完全不同。

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

LinkedList 是由鏈表組成,每個(gè)節(jié)點(diǎn)又有頭尾兩個(gè)節(jié)點(diǎn)分別引用了前后兩個(gè)節(jié)點(diǎn);因此它也是一個(gè)雙向鏈表。

所以理論上來說它的寫入非常高效,將不會(huì)有 ArrayList 中效率極低的數(shù)組復(fù)制,每次只需要移動(dòng)指針即可。

這里偷懶就不畫圖了,大家自行腦補(bǔ)下。

對(duì)比測試

坊間一直流傳:

LinkedList 的寫入效率高于 ArrayList,所以在寫大于讀的時(shí)候非常適用于 LinkedList 。

  1. @Benchmark    @BenchmarkMode(Mode.AverageTime)    @OutputTimeUnit(TimeUnit.MICROSECONDS)    public void linkedList() {        List<String> array = new LinkedList<>();        for (int i = 0; i < TEN_MILLION; i++) {            array.add("123");        }    } 

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

這里測試看下結(jié)論是否符合;同樣的也是對(duì) LinkedList 寫入 1000W 次數(shù)據(jù),通過結(jié)果來看初始化數(shù)組長度的 ArrayList 效率明顯是要高于 LinkedList 。

但這里的前提是要提前預(yù)設(shè) ArrayList 的數(shù)組長度,避免數(shù)組擴(kuò)容,這樣 ArrayList 的寫入效率是非常高的,而 LinkedList 的雖然不需要復(fù)制內(nèi)存,但卻需要?jiǎng)?chuàng)建對(duì)象,變換指針等操作。

而查詢就不用多說了, ArrayList 可以支持下標(biāo)隨機(jī)訪問,效率非常高。

LinkedList 由于底層不是數(shù)組,不支持通過下標(biāo)訪問,而是需要根據(jù)查詢 index 所在的位置來判斷是從頭還是從尾進(jìn)行遍歷。

 

項(xiàng)目里用到 Java 集合時(shí),應(yīng)該避免的一些坑

 

但不管是哪種都得需要移動(dòng)指針來一個(gè)個(gè)遍歷,特別是 index 靠近中間位置時(shí)將會(huì)非常慢。

 

總結(jié)

高性能應(yīng)用都是從小細(xì)節(jié)一點(diǎn)點(diǎn)堆砌起來的,就如這里提到的 ArrayList 的坑一樣,日常使用沒啥大問題,一旦數(shù)據(jù)量起來所有的小問題都會(huì)成為大問題。

所以再總結(jié)下:

  • 再使用 ArrayList 時(shí)如果能提前預(yù)測到數(shù)據(jù)量大小,比較大時(shí)一定要指定其長度。
  • 盡可能避免使用 add(index,e) api,會(huì)導(dǎo)致復(fù)制數(shù)組,降低效率。
  • 再額外提一點(diǎn),我們常用的另一個(gè) Map 容器 HashMap 也是推薦要初始化長度從而避免擴(kuò)容。

 

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2022-12-27 14:52:31

Kubernetes云原生開發(fā)

2021-06-08 06:13:16

React開發(fā)開發(fā)技術(shù)

2021-06-30 07:19:35

微服務(wù)業(yè)務(wù)MySQL

2010-10-08 16:32:59

MySQL語句

2019-07-10 08:56:50

Java技術(shù)容器

2019-07-11 10:42:57

容器ArrayList JMH

2020-08-07 08:04:03

數(shù)據(jù)庫MySQL技術(shù)

2021-04-16 08:11:24

js前端JavaScript

2023-03-28 08:14:42

系統(tǒng)優(yōu)化指標(biāo)

2010-06-11 09:19:23

jQuery

2018-05-30 12:04:36

LinuxUbuntu 18.0

2014-08-07 09:40:46

2011-06-01 16:50:21

JAVA

2018-03-26 20:00:32

深度學(xué)習(xí)

2011-08-01 10:37:29

軟件項(xiàng)目管理

2009-06-22 15:36:00

如何學(xué)好java

2009-06-18 09:51:25

Java繼承

2009-07-09 13:16:54

Java Swing開

2023-08-08 08:12:25

圖形編輯器幾何算法

2015-08-24 09:26:18

Java內(nèi)存問題見解
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)