由一個簡單算法想到的程序員素養問題


  題記:五月從帝都回到武漢,旅游半個月后開始找新工作,六月選擇了一家華中地區為數不多的移動互聯網公司入職至今,略有感觸——比較帝都碼農與武漢碼農的平均水平,就跟兩個城市的經濟發展水平差異一樣大,不是說武漢這邊沒有優秀的程序員(我也算半個嘛),而是說平均水平確實不如其他一線城市。想想也正常,巨頭公司都扎堆北上廣深,以極具競爭力的薪酬福利和巨頭光環吸引着廣大程序員,反觀武漢的互聯網發展尚處在初級階段,無論從公司規模、名氣還是最實際的薪酬福利方面均不如一線城市,自然無法吸引廣大程序員咯。本人在新公司待了近三個月,慢慢發現代碼中的一些問題,而這些問題中除開因為個人能力短時間無法解決的,剩下的就是我想探討的程序員素養問題。

  先說下我所認為的程序員素養吧,我認為應包括以下幾個方面呢:1,扎實的功底——扎實掌握操作系統,數據結構等大學基本課程,鼓吹基礎無用論的,必定是個三流程序員。2,學習心態——對你所調用的,一定要有好奇心,盡可能花時間去了解API背后的東西,去了解系統框架是如何運作;對新的領域,一定要有好奇心,多嘗試多學習,他山之石,可以攻玉。3,善於自學、總結、發問——工作后沒有人去手把手教你,只要自己刻苦專研,並總結成博客或者筆記之類的成果,才又提高;在遇到自己能力范圍內解決不了的問題,要善於向牛人請求點播,而一針見血的問題往往也會收獲一針見血的回答。

  舉一個簡單的例子來討論素養1,需求如下:已經完成排序的1萬條數據,現在需要取前5000條作為最終數據,考慮到數據量較大,如果是新建列表,遍歷舊列表的5000條並重新建立引用,有(引用類型所占字節 * 5000)字節的空間上的浪費,而且這種方式顯得比較笨拙。

  所以,略加思索后寫出了以下代碼:

List<Item> rankItems = new ArrayList<Item>();
// 生成數據+排序
...
while (rankItems.size() > 5000) {
    rankItems.remove(rankItems.size() - 1);
}

  簡單分析,對於ArrayList大家都不陌生,是以數組方式實現的鏈表,remove(index)方法實質上調用的是System.arraycopy()方法,源碼如下:

    /**
     * Removes the object at the specified location from this list.
     *
     * @param index
     *            the index of the object to remove.
     * @return the removed object.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || >= size()}
     */
    @Override public E remove(int index) {
        Object[] a = array;
        int s = size;
        if (index >= s) {
            throwIndexOutOfBoundsException(index, s);
        }
        @SuppressWarnings("unchecked") E result = (E) a[index];
        System.arraycopy(a, index + 1, a, index, --s - index);
        a[s] = null;  // Prevent memory leak
        size = s;
        modCount++;
        return result;
    }

  System.arraycopy()是native方法,調用的應該是C語言中的memcpy(),雖然copy的source地址和dest地址都是相同的,無需分配新內存,但是要經過5000次的內存IO讀寫才能刪除ArrayList中的數據。究其原因是因為ArrayList的數組實現方式,不利於在指定位置做添加/刪除操作,所以思考后有了以下代碼:

List<Item> rankItems = new LinkedList<Item>();
// 生成數據+排序
...
while (rankItems.size() > 5000) {
    rankItems.remove(rankItems.size() - 1);
}

  再分析LinkedList的刪除效率,是否比ArrayList高了呢?LinkedList的remove(index)方法實現如下:

/**
     * Removes the object at the specified location from this {@code LinkedList}.
     *
     * @param location
     *            the index of the object to remove
     * @return the removed object
     * @throws IndexOutOfBoundsException
     *             if {@code location < 0 || >= size()}
     */
    @Override
    public E remove(int location) {
        if (0 <= location && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            Link<E> previous = link.previous;
            Link<E> next = link.next;
            previous.next = next;
            next.previous = previous;
            size--;
            modCount++;
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

  LinkedList的刪除操作很簡單,只需要修改指定index對象其前后對象的引用即可,但是在指針移動到指定index之前,需要移動1/2個列表長度,效率並非最高,能否將移動的操作也簡化掉呢?既然是已排好序的列表,每次刪除都刪除列表末尾的對象,那我們可以使用LinkedList提供的removeLast()方法,代碼如下:

List<Item> rankItems = new LinkedList<Item>();
// 生成數據+排序
...
while (rankItems.size() > 5000) {
    rankItems.removeLast();
}

  分析removeLast()的效率,LinkedList的removeLast()方法實現如下:

/**
     * Removes the last object from this {@code LinkedList}.
     *
     * @return the removed object.
     * @throws NoSuchElementException
     *             if this {@code LinkedList} is empty.
     */
    public E removeLast() {
        Link<E> last = voidLink.previous;
        if (last != voidLink) {
            Link<E> previous = last.previous;
            voidLink.previous = previous;
            previous.next = voidLink;
            size--;
            modCount++;
            return last.data;
        }
        throw new NoSuchElementException();
    }

  LinkedList的removeLast()方法沒有移動指針的操作,只需要借助於列表尾的voidLink,即可完成對列表尾部對象的刪除,效率上較高。

 

  公司的大牛review了相關代碼,提議使用AbstractList的subList(start, end)方法直接范圍所需數據,我看了下源碼,subList()相當於在不修改數據源的情況下,設定start, end並Override相關方法及Iterator,形成了原列表的一個“視圖”,將原列表的可見范圍限定[start, end)的區間內。總結一下,如果接下來的代碼只是使用列表中的部分數據,而剩下數據又不是很占用內存的情況下,確實用subList(start, end)更好,連指針操作都省去了~

 

結束語:一個程序員一定要對自己的代碼負責到底,優化,不僅對業務有所提升,更多的是在思考優化的過程中對提升自己的功力很有幫助。最近閑時時常思考自己的職業規划與出路,迷茫之中終有堅定,堅持做一個好的程序員,一定會有美好的明天!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM