天天看點

猿思考系列9——一文擷取隐藏邏輯挖掘辦法

看完上一個章節,相信你已經充分的掌握了資料庫事務的一些事情,猿人工廠君也知道,内容對于新手而言,了解起來還是比較很吃力的,文中提到的原理和内容,有興趣的可以和我一起探讨,猿人工廠君就不一一贅述了。之前有小夥伴反應,資料結構的知識比較薄弱,今天我們就來探讨下,通過思考的方式,來解決基本的資料結構問題。本文是思考系列的最後一章,從下一章節開始,帶你玩兒架構和實戰完成猿蛻變!

猿思考系列9——一文擷取隐藏邏輯挖掘辦法
猿思考系列9——一文擷取隐藏邏輯挖掘辦法
猿思考系列9——一文擷取隐藏邏輯挖掘辦法
猿思考系列9——一文擷取隐藏邏輯挖掘辦法

相信大家都使用過無數次ArrayList了,一個大家常用的集合容器——可變數組。既然是容器,那自然是什麼東西都能存放的啦。數組一般來說都是長度不變的,那要做到長度可變該怎麼辦呢?

猿思考系列9——一文擷取隐藏邏輯挖掘辦法

開動你的小腦筋,既然要存放任何資料,那自然是Object比較可靠啦,對象之祖,可以表示仍和資料。長度可變,自然是存放滿了之後,改變數組長度了。

猿思考系列9——一文擷取隐藏邏輯挖掘辦法

嗯,在數組種放入資料确實簡單,但是依然是有一些隐藏邏輯的。想想看,比如數組越界的處理,比如增加或删除資料後,原有資料發生移動,比如資料存放時,恰巧遇到資料滿了之後,原有資料需要拷貝和移動。以上這些都是數組操作的隐藏邏輯噢,發現這些隐藏邏輯,對你的編碼能力和業務分析能力的提升是很有幫助的。下面是一個簡單的實作,拿走不謝噢。

package com.pz.collection.demo;
 
/**
 * pangzi
 * 模拟實作ArrayList
 */
public class ArrayList {
    //使用數組存放資料
    privateO bject[] elementData;
    //數組大小
    private int size;
 
 
    /**
     *無參構造方法 預設大小10
     */
    public ArrayList(){
 
       this(10);
    }
 
    /**
     *
     * @paraminitialCapacity
     */
    public ArrayList(int initialCapacity){
 
       if(initialCapacity<0){
           try {
               throw new Exception();
            }catch (Exception e) {
               e.printStackTrace();
            }
        }
       elementData=new Object[initialCapacity];
    }
 
    /**
     * 判斷容器是否為空
     * @return
     */
    public boolean isEmpty(){
 
        return size==0;
    }
 
    /**
     * 根據下标擷取元素
     * @paramindex
     * @return
     */
    public Object get(int index){
       rangeCheck(index);
        return elementData[index];
    }
 
    /**
     * 預設增加在數組尾部增加元素
     * @paramobj
     * @return
     */
    public boolean add(Object obj){
       ensureCapacity();
       elementData[size]=obj;
       size++;
        return true;
    }
 
    /**
     * 在指定下标增加元素
     * @paramindex
     * @paramobj
     */
    public void add(int index,Object obj){
       rangeCheck(index);
       ensureCapacity();
       System.arraycopy(elementData, index, elementData, index+1,size-index);
       elementData[index]=obj;
       size++;
    }
 
    /**
     * 删除指定下标元素
     * @param index
     * @return
     */
    public Object remove(int index){
       rangeCheck(index);
        int arrnums=size-index-1;
        ObjectoldValue=elementData[index];
       if(arrnums>0){
           System.arraycopy(elementData, index+1, elementData,index, arrnums);
        }
       elementData[--size]=null;
        return oldValue;
    }
 
    /**
     * 根據對象删除元素
     * @paramobj
     * @return
     */
    public boolean remove(Object obj){
       for(int i=0;i<size;i++){
           if(get(i).equals(obj)){
               remove(i);
            }
           break;
        }
        return true;
    }
 
    /**
     * 根據下标改變對象并傳回舊的值
     *
     * @paramindex 下标
     * @paramobj   元素
     * @return
     */
    public Object set(int index,Object obj){
       rangeCheck(index);
        ObjectoldValue=elementData[index];
       elementData[index]=obj;
        return oldValue;
    }
 
    /**
     * 範圍檢查
     * @paramindex 下标
     */
    private void rangeCheck(int index){
 
       if(index<0||index>=size){
           try {
               throw new Exception();
            }catch (Exception e) {
               e.printStackTrace();
            }
        }
    }
 
    /**
     * 數組擴容檢查
     */
    private void ensureCapacity(){
       if(size==elementData.length){
           Object[] newArray=new Object[size*2+1];
           System.arraycopy(elementData, 0, newArray, 0, elementData.length);
           elementData=newArray;
        }
    }
 
    /**
     * 傳回元素第一次出現下标
     * 如果無元素傳回-1
     * @paramobj
     * @return
     */
    public int indexOf(Object obj) {
        if(obj == null) {
           for (int i = 0; i < size; i++){
               if (elementData[i]==null){
                   return i;
}
}
 
        } else{
           for (int i = 0; i < size; i++){
               if (obj.equals(elementData[i])){
                   return i;
                }
}
        }
        return-1;
    }
 
    /**
     * 傳回元素最後一次出現下标
     * 如果無元素傳回-1
     * @paramobj
     * @return
     */
    public int lastIndexOf(Object obj) {
        if(obj != null) {
           for (int i = size-1; i >= 0; i--) {
               if (obj.equals(elementData[i])) {
                   return i;
               }
            }
        } else{
 
           for (int i = size-1; i >= 0; i--) {
               if (elementData[i] == null) {
                   return i;
               }
            }
        }
        return-1;
    }
 
    /**
     * 傳回List大小
    * @return
     */
    public intsize(){
 
        return size;
    }
}           

複制

數組的實作已經學會了,那麼數組有什麼特點呢?自然是連續存儲,查找效率較高,但是插入和删除因為發生資料移動的關系,效率會低一些,記住了。面試官經常問的噢。

猿思考系列9——一文擷取隐藏邏輯挖掘辦法
猿思考系列9——一文擷取隐藏邏輯挖掘辦法

連結清單是一種實體存儲單元上非連續、非順序的存儲結構,資料元素的邏輯順序是通過連結清單中的指針連結次序實作的。連結清單由一系列結點(連結清單中每一個元素稱為結點)組成,結點可以在運作時動态生成。每個結點包括兩個部分:一個是存儲資料元素的資料域,另一個是存儲下一個結點位址的指針域。。

學會了解概念也是一種本事呢。指針,在java裡是不存在的,但是有一個替代品——引用類型。那怎樣去實作一個連結清單呢?自然是定義一個類,一個Object類型的屬性,用于存放資料,一個next屬性,引用自身,指向下一條資料就好了。這樣一條資料,指向下一條資料的結構,不就像自行車鍊條一樣嗎?如果我們要做插入操作,找到需要插入的位置,改變引用的位置,插入位置的下一個節點,指向新資料,新資料的下一個節點,指向老資料的下一個節點就好了,資料不需要發生移動。删除也是一樣的,找到需要删除的節點,讓被删除的節點的上一個節點,指向被删除的節點的下一個節點就好了。下面是以一個簡單的單向連結清單實作,你可以參考,也可以另行實作。

package com.pz.collection.demo;
 
/**
 * 節點
 * pangzi
 */
public class Node{
 
    public Object data;//元素
    public Node next;//指針
    public Node(Object data, Node next) {
       this.data = data;
       this.next = next;
    }
    public Node(){
       this(null,null);
    }
    public Node(Object data){
       this(data,null);
    }
 
    @Override
    public String toString() {
        return"Node [data=" + data + "]";
    }
}           

複制

package com.pz.collection.demo;
 
 
/**
 * 單連結清單
 */
public class LinkedList {
 
    private Node headNode;
    private int size;
    public LinkedList(){
       headNode = new Node(null,null);
        size =0;
    }
    public int getSize(){
        return size;
    }
    public boolean isEmpty(){
        return size == 0;
    }
    public void addFirst(Object obj){
 
        add(0,obj);
    }
    public void addLast(Object obj){
       add(size,obj);
    }
    public void add(int index,Object obj){
       if(index < 0 || index > size){
           throw new IllegalArgumentException("索引越界異常");
        }
        Nodeprev = headNode;
       for(int i = 0 ; i < index ; i++){
           prev = prev.next;
        }
       prev.next = new Node(obj,prev.next);
        size++;
    }
    public Object get(int index){
       if(index < 0 || index > size){
            throw newIllegalArgumentException("索引越界異常");
        }
        Nodecur = headNode.next;
       for(int i = 0 ; i < index ; i++){
           cur = cur.next;
        }
        return cur.data;
    }
    public Object getFirst(){
        return get(0);
    }
    public Object getLast(){
        return get(size-1);
    }
    public void update(int index,Object obj){
       if(index < 0 || index > size){
           throw new IllegalArgumentException("索引越界異常");
        }
        Nodecur = headNode.next;
       for(int i = 0 ; i < index ; i++){
           cur = cur.next;
        }
       cur.data = obj;
    }
    public boolean contains(Object obj){
        Nodenode = headNode.next;
        while(node != null){
            if(node.data.equals(obj)){
               return true;
            }
           node = node.next;
        }
        return false;
    }
    public void delete(int index){
       if(index < 0 || index > size){
           throw new IllegalArgumentException("索引越界異常");
        }
        Nodeprev = headNode;
       for(int i = 0 ; i < index ; i++){
           prev = prev.next;
        }
        Nodecur = prev.next;
       prev.next = cur.next;
       cur.next = null;
       size--;
    }
    public void deleteFirst(){
       delete(0);
    }
    public void deleteLast(){
       delete(size-1);
    }
    public void deleteElement(Object obj){
        Nodeprev = headNode;
       while(prev.next != null){
           if(prev.next.data.equals(obj))
               break;
           prev = prev.next;
        }
       if(prev.next != null){
           Node delNode = prev.next;
           prev.next = delNode.next;
           delNode.next = null;
            size --;
        }
    }
}           

複制

猿思考系列9——一文擷取隐藏邏輯挖掘辦法
猿思考系列9——一文擷取隐藏邏輯挖掘辦法
猿思考系列9——一文擷取隐藏邏輯挖掘辦法
猿思考系列9——一文擷取隐藏邏輯挖掘辦法

相信你已經看出來了,上面的結構就是HashMap。其實最基本的資料結構從來就隻有兩種:連續存儲的數組和非連續存儲的單連結清單。其餘所有的資料結構,都來自二者的組合。

HashMap就是這樣的一個結構,一個數組,用于存放Entry,Entry通過key進行hash算法快速的定位下标,如果出現hash沖突,那麼将發生沖突的節點,挂到相同hash值的Entry的後面即可。這樣一來,查詢找節點時通過key,進行hash計算,快速定位出元素數組裡的下标,如果沒有立刻找到,再周遊對應的連結清單就好了,插入和删除,都不需要移動元素。算法的好壞關鍵,就在于hash沖突的多少,越少的沖突,帶來越好的性能。

下面就是一個HashMap的簡單實作,你也可以嘗試自己寫一下。

package com.pz.collection.demo;
 
public class HashMap<K, V> {
    private int capacity = 16;
    private int size = 0;
    private Entry[] table;
 
    public HashMap(int capacity) {
       this.capacity = capacity;
       this.table = new Entry[capacity];
    }
 
    public HashMap() {
       this.table = new Entry[capacity];
    }
 
    public V put(K key, V value) {
        if(key == null) {
           return putNullKey(value);
        }
        inthash = hash(key);
        int i= indexTable(hash, capacity);
 
        for(Entry<K, V> e = table[i]; e != null; e = e.next) {
            if(e.hash == hash && (e.key == key || e.key.equals(key))) {
               V old = e.value;
               e.value = value;
                return old;
            }
        }
 
       addEntry(hash, key, value, i);
 
        returnnull;
    }
 
    public V get(K key) {
        if(key == null)
           return getNullKey();
 
        inthash = hash(key);
        int i= indexTable(hash, capacity);
 
        for(Entry<K, V> e = table[i]; e != null; e = e.next) {
            if(e.hash == hash && (e.key == key || e.key.equals(key)))
               return e.value;
        }
 
        return null;
    }
 
    private V getNullKey() {
        for(Entry<K, V> e = table[0]; e != null; e = e.next) {
            if(e.key == null)
               return e.value;
        }
        return null;
    }
 
    private void addEntry(int hash, K key, V value, int i) {
       Entry<K, V> e = table[i];
       table[i] = new Entry<K, V>(hash, key, value, e);
        if(size == capacity)
           resize();
 
       size++;
    }
 
    private void resize() {
       capacity = capacity * 2;
       Entry[] newtable = new Entry[capacity];
        for(Entry<K, V> entry : table) {
           Entry<K, V> e = entry; // 取得舊Entry數組的每個元素
            if(e != null) {
               entry = null;// 釋放舊Entry數組的對象引用(循環後,舊的Entry數組不再引用任何對象)
               do {
                   Entry<K, V> next = e.next;
                   int i = indexTable(e.hash, capacity); // 重新計算每個元素在數組中的位置
                   e.next = newtable[i];
                   newtable[i] = e; // 将元素放在數組上
                   e = next; // 通路下一個Entry鍊上的元素
               } while (e != null);
            }
        }
        table= newtable;
    }
 
    private int indexTable(int hash, int capacity) {
        returnhash & (capacity - 1);
    }
 
    private int hash(K key) {
        if(key == null)
           return 0;
 
        int h= key.hashCode();
        h = h^ (h >>> 16);
 
        returnh;
    }
 
    private V putNullKey(V value) {
        for(Entry<K, V> e = table[0]; e != null; e = e.next) {
            if(e.key == null) {
                V old = e.value;
               e.value = value;
               return old;
            }
        }
 
       addEntry(0, null, value, 0);
 
        return null;
    }
 
}
 
class Entry<K, V> {
    public int hash;
    public K key;
    public V value;
    public Entry<K, V> next;
 
    public Entry(int hash, K key, V value, Entry<K, V> next) {
       this.hash = hash;
       this.key = key;
       this.value = value;
       this.next = next;
    }
}           

複制