Java ArrayList 踩坑記錄

來源:互聯網
上載者:User

標籤:contain   無限   就會   複製   原理   car   tca   blog   data   

  做編程的一個常識:不要在迴圈過程中刪除元素本身(至少是我個人的原則)。否則將發生不可預料的問題。

  而最近,看到一個以前的同學寫的一段代碼就是在迴圈過程中刪除元素,我很是納悶啊。然後後來決定給他改掉。然後引發了另外的慘案。

  原來的代碼是這樣的:

    public List<A> getUserDebitCard(A cond) {        List<A> list=userService.getCard(cond);        List<A> result=null;        if(list!=null && list.size()>0){            Collections.sort(list, new Comparator<A>(){                public int compare(A b1, A b2) {                //按時間排序                if(Integer.valueOf(b1.getAddTime()) > Integer.valueOf(b2.getAddTime())){                     return -1;                    }                     return 1;                  }             });             A bean = getA(cond);             result=new ArrayList<A>();             if(bean!=null){                 for (int i = 0; i < list.size(); i++) {                    if(list.get(i).getCardNum().equals(bean.getCardNum())){                        list.get(i).setAs(1);                        result.add(list.get(i));                        list.remove(i);                    }else{                        list.get(i).setAs(0);                    }                 }             }                result.addAll(list);         }        return result;    }

  看了如上代碼,我很是鬱悶,然後給改成如下:

    public List<A> getUserDebitCard(A cond) {        List<A> list=userService.getCard(cond);        List<A> result=null;        if(list!=null && list.size()>0){            Collections.sort(list, new Comparator<A>(){                public int compare(A b1, A b2) {                //按時間排序                if(Integer.valueOf(b1.getAddTime()) > Integer.valueOf(b2.getAddTime())){                     return -1;                    }                     return 1;                  }             });             A bean = getA(cond);             result=new ArrayList<>();             if(bean != null){                 // 將上次的卡放置在第一位                 Integer lastAIndex = 0;                 Integer listSize = list.size();                 if(listSize > 0) {                     for (int i = 0; i < listSize; i++) {                         if (list.get(i).getCardNum().equals(bean.getCardNum())) {                             list.get(i).setAs(1);                             result.add(list.get(i));        //將排在首位的元素先添加好,並記錄下index                             lastAIndex = i;//                             list.remove(i);                //迴圈過程中刪除元素是危險的                         } else {                             list.get(i).setAs(0);                         }                     }                     list.remove(lastAIndex);                 }             }                 result.addAll(list);                            //在迴圈外刪除元素,以為萬事大吉,結果悲劇了,這裡居然添加了兩個元素進來                         }        return result;    }

  這下出事了,原本只有一個元素的result,現在變成了兩個了,這是為什麼呢?媽蛋,我明明已經remove掉了啊。

  也想過百度一下,但是木有搞定啊。然後,拿出看家絕招,斷點調試,進入list.remove(Integer) 方法。其源碼如下:

    /**     * Removes the first occurrence of the specified element from this list,     * if it is present.  If the list does not contain the element, it is     * unchanged.  More formally, removes the element with the lowest index     * <tt>i</tt> such that     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>     * (if such an element exists).  Returns <tt>true</tt> if this list     * contained the specified element (or equivalently, if this list     * changed as a result of the call).     *     * @param o element to be removed from this list, if present     * @return <tt>true</tt> if this list contained the specified element     */    public boolean remove(Object o) {        if (o == null) {            for (int index = 0; index < size; index++)                if (elementData[index] == null) {                    fastRemove(index);                    return true;                }        } else {            for (int index = 0; index < size; index++)                if (o.equals(elementData[index])) {                    fastRemove(index);                    return true;                }        }        return false;    }

  原來,由於我使用Integer作為刪除元素的條件,這裡把Integer當作一個元素去比較了,而並不是平時我們以為的自動拆裝箱變為int了。 而進入這個方法的意思,是要找到和元素內容相等的元素,然後刪除它。而我給的是一個索引,自然就不相等了,沒有刪除也是自然了,因為就會看到重複的元素出來了。

  我們再來看一下根據索引刪除元素的源碼,對比一下:

    /**     * Removes the element at the specified position in this list.     * Shifts any subsequent elements to the left (subtracts one from their     * indices).     *     * @param index the index of the element to be removed     * @return the element that was removed from the list     * @throws IndexOutOfBoundsException {@inheritDoc}     */    public E remove(int index) {        rangeCheck(index);        modCount++;        E oldValue = elementData(index);        int numMoved = size - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,                             numMoved);        elementData[--size] = null; // clear to let GC do its work        return oldValue;    }

  從這裡我們可以看到,根據索引刪除ArrayList元素的原理是,將原來的數組排除要刪除的索引,然後複製到新的數組去,然後將最後一個元素置為null,留給GC回收。並把刪除的元素返回。

  這樣,一下就明白了刪除ArrayList元素到底是咋麼一回事了。修複方案就簡單了,將Integer替換為int即可:

        // Integer lastAIndex = 0;   //替換成int        int lastAIndex = 0;

  當然了,最後,我還是換成了另一個更穩妥的方案了,用另一個容器接收最終的結果

    public List<A> getUserDebitCard(A cond) {        List<A> list=userService.getCard(cond);        List<A> result=null;        if(list!=null && list.size()>0){            Collections.sort(list, new Comparator<A>(){                public int compare(A b1, A b2) {                //按時間排序                if(Integer.valueOf(b1.getAddTime()) > Integer.valueOf(b2.getAddTime())){                     return -1;                    }                     return 1;                  }             });             A bean = getA(cond);             result=new ArrayList<>();             if(bean != null){                 // 將上次的卡放置在第一位                 for (A card1 : list) {                     if (card1.getCardNum().equals(bean.getCardNum())) {                         card1.setAs(1);                         result.add(0, card1);    //直接插入第一位即可//                         list.remove(i);                //迴圈過程中刪除元素是危險的                     } else {                         card1.setAs(0);                         result.add(card1);                     }                 }             }                         }        return result;    }

  雖然自動拆裝箱很方便,也很實用,但是有時一不小心就會把自己給埋坑裡了,當心了。尤其是針對線上問題。

  畢竟,代碼中的一點點小問題,一到線上就會被無限放大,不可掉以輕心啊!

 

Java ArrayList 踩坑記錄

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.