自定義LinkedList(二十二)

勿以惡小而爲之,勿以善小而不爲--------------------------劉備

勸諸君,多行善事積福報,莫作惡

上一章簡單介紹了 Java實現複數(二十一),如果沒有看過,請觀看上一章

一. 自定義LinkedList

LinkedList 位於 java.util 包下, 內部封裝的是鏈接, 用於快速 添加,刪除元素,也支持查詢元素。

由於是內部封裝鏈表,數據結構需要多看一下。

LinkedList 的源碼非常好,建議多讀一下。

老蝴蝶簡單自定義一個 LinkedList, 實現相應的基礎功能。

一.一 自定義 MyLinkedList, 實現 LinkedList 功能

package com.yjl.collection;

/**
 * package: com.yjl.collection
 * className: MyLinkedList
 * Description: 請輸入相應的描述
 *
 * @author : yuezl
 * @Date :2020/6/11 11:35
 */
public class MyLinkedList<E>{
    /*內部定義一個Node. 靜態的*/
    private static class Node<E>{
        /**
         * @param previous 上一節點
         * @param next 下一節點
         * @param e 當前元素
         *
         */
        private Node<E> previous;

        private Node<E> next;

        private E e;

        public Node(){

        }

        public Node(Node previous,E e,Node next){
            this.previous=previous;
            this.e=e;
            this.next=next;
        }
    }

    /*需要定義三個屬性, 根節點,尾節點,和長度size*/

    /**
     * @param first 根節點
     * @param last 尾節點
     * @param size 當前長度
     *
     * 如果沒有元素的話, first=last=null,
     *  如果只有一個元素的話, first=last=唯一的那個元素值構成的Node
     *  如果有兩個或者兩個以上的話, first指向第一個,last指向最後一個
     */
    private Node<E> first;

    private Node<E> last;

    private int size;



    /*定義構造方法*/

    public MyLinkedList(){


    }

    public MyLinkedList(MyLinkedList<E> mll){

        addAll(mll);

    }

    /*關於添加的相應操作*/

    /**
     * 添加 到最後
     * @param e
     * @return
     */
    public boolean add(E e){
        addLast(e);
        return true;

    }

    /**
     * 有索引的添加
     * @param index
     * @param e
     * @return
     */
    public boolean add(int index,E e){

        //如果是最後一個,那麼就是追加
        if(index==size){
            addLast(e);
        }else{
            addBefore(e,getNodeByIndex(index));
        }
        return true;
    }

    public boolean addAll(MyLinkedList<E> mll){

        addAll(size,mll);
        return true;

    }

    public boolean addAll(int index,MyLinkedList<E> mll){
        //轉換成數組,獲取裏面的元素。
        Object[] obj=mll.toArray();

        int length=obj.length;

        if(length==0){
            throw new NullPointerException("傳入的集合爲空");
        }


        //定義兩個對象,一個上一個對象,一個當前對象。

        Node<E> pre,local;

        //說明是後面插入的,或者調用 addll()方法的。
        if(index==size){

            pre=last;

            //當前的爲空
            local=null;

        }else{

            local=getNodeByIndex(index);

            pre=local.previous;

        }


        for(int i=0;i<obj.length;i++){
                //構建新對象
                Node<E> node=new Node(pre,obj[i],null);

                //判斷一下,是否是開頭插入

                if(pre==null){
                    first=node;
                }else{
                    //上一個的下一個爲node
                    pre.next=node;
                }
                //新的pre
                pre=node;

        }

        //判斷一下,是否是末尾插入

        if(local==null){

            last=pre;

        }else{

            pre.next=local;

            local.previous=pre;
        }

        size+=length;

        return true;

    }


    /**
     * 添加到第一個
     * @param e
     */
    public void addFirst(E e){

        // 構建Node,記錄一下,以前的第一個。 以前的第一個,將會變成新Node 的next
        Node<E> f=first;

        Node<E> node=new Node(null,e,f);

        first=node;

        //看以前是否有內容,來判斷是否有下一個。

        if(f==null){
            //以前沒有值的話, last爲現在的node
            last=node;
        }else{
            //設置 以前的previous 爲node, 以前previous爲null
            f.previous=node;
        }
        size++;


    }

    /**
     * 添加到最後一個
     * @param e
     */
    public void addLast(E e){

        //先構建 Node,記錄一下,以前的最後一個。 以前的最後一個,將變成新的前。

        Node<E> l=last;

        //新Node

        Node node=new Node(l,e,null);

        //最後一個,last 指向新的 node

        last=node;

        //判斷一下 l, 由此確定一下 first
        if(l==null){
            //根節點爲  node.
            first=node;
        }else{
            //以前的下一個爲 last, 以前是null
            l.next=last;
        }
        size++;

    }

    /**
     * 在以前的任意結點上添加元素,不適用末尾添加。
     * @param e
     * @param node
     */
    private void addBefore(E e,Node<E> node){

        //獲取該節點的上一個節點。 下一個接點,是不變的。
        final Node prev=node.previous;
        //構建新的Node 新結點的上一個結點爲prev,下一個接點爲當前結點。
        Node newNode=new Node(prev,e,node);

        //將上一個結點變成新結點。
        node.previous=newNode;

        if(prev==null){

            first=newNode;

        }else{
            prev.next=newNode;
        }

        size++;
    }


    /**
     * 放置元素, 放置在最前面
     * @param e
     */

    public void push(E e){

       addFirst(e);
    }

    /**
     *  移除元素, 移除第一個。
     * @return
     */
    public E pop(){

        return unLinkFirst(first);
    }

    /** 移除的操作*/

    /**
     * 移除第一個
     * @return
     */
    public E remove(){
      return removeFirst();
    }

    /**
     * 移除固定索引的元素
     * @param index
     * @return
     */
    public E remove(int index){
        return unLink(getNodeByIndex(index));
    }

    /**
     * 按照對象移除
     * @param obj
     * @return
     */
    public E remove(Object obj){
        int index=indexOf(obj);
        if(index>=0){
            return remove(index);
        }
        return null;
    }

    /**
     * 移除第一個
     * @return
     */
    public E removeFirst(){
       return  unLinkFirst(first);
    }

    /**
     * 移除最後一個
     * @return
     */
    public E removeLast(){
        return unLinkLast(last);
    }

    /**
     * 移除中間的元素
     * @param node
     * @return
     */
    private E unLink(Node<E> node){

        //獲取上一個,獲取下一個。 上一個和下一個,均是有值的。
        Node<E> pre=node.previous;

        Node<E> next=node.next;

        E element=node.e;


        node.e=null;

        //前面爲空,說明只有一個元素
        if(pre==null){
            first=null;

        }else{
            pre.next=next;

            //當前元素斷開
            node.previous=null;
        }
        //最後一個時
        if(next==null){
            last=pre;
        }else{
            pre.next=next;
            node.next=null;
        }

        size--;

        return element;
    }

    /**
     * 移除頭部
     * @param node
     * @return
     */
    private E unLinkFirst(Node<E> node){
        //獲取下一個元素
        Node next=node.next;

        E element=node.e;

        //GC
        node.e=null;
        node.next=null;

        //first 就指向下一個元素了
        first=next;

        if(next==null){

            last=null;

        }else{

            //令下一個元素的上一個元素爲null,下一個元素不變
            next.previous=null;

        }
        size--;


        return element;
    }

    /**
     * 移除最後一個
     * @param node
     * @return
     */
    private E unLinkLast(Node<E> node){
        //獲取前面的元素
        Node pre=node.previous;

        E element=node.e;

        node.previous=null;

        node.e=null;

        last=pre;

        //看是否是第一個
        if(pre==null){

            first=null;


        }else{
            pre.next=null;

        }



        size--;

        return element;


    }

    /*設置和獲取*/

    /**
     * 獲取長度
     * @return
     */
    public int size(){
        return size;
    }

    /**
     * 判斷是否爲空
     * @return
     */
    public boolean isEmpty(){
        return size==0?true:false;
    }

    /**
     * 是否包含某個對象
     * @param obj
     * @return
     */
    public boolean contains(Object obj){
        return indexOf(obj)>=0?true:false;
    }

    /**
     * 索引位置
     * @param obj
     * @return
     */
    public int indexOf(Object obj){

        int index=-1;

        //對象爲null
        if(obj==null){

            for(Node<E> node=first;node!=null;){

                index++;


                if(node.e==obj){
                    return index;
                }

                node=node.next;
            }

        }else{

            for(Node<E> node=first;node!=null;){

                index++;

                if(obj.equals(node.e)){
                    return index;
                }

                node=node.next;
            }
        }

        return -1;


    }

    /**
     * 倒序的索引位置
     * @param obj
     * @return
     */
    public int lastIndexOf(Object obj){

        int index=-1;

        //對象爲null
        if(obj==null){

            for(Node<E> node=last;node!=null;){

                index++;


                if(node.e==obj){
                    return index;
                }

                node=node.previous;
            }

        }else{

            for(Node<E> node=last;node!=null;){

                index++;

                if(obj.equals(node.e)){
                    return index;
                }

                node=node.previous;
            }
        }

        return -1;


    }

    /**
     * 設置值
     * @param index
     * @param e
     * @return
     */
    public E set(int index,E e){

        Node old=getNodeByIndex(index);

        old.e=e;
        return (E)old.e;

    }

    /**
     * 獲取值
     * @param index
     * @return
     */
    public E get(int index){

        Node node=getNodeByIndex(index);

        return (E)node.e;

    }

    /**
     * 得到頭部
     * @return
     */
    public E getFirst(){

        if(first==null){
            return null;
        }

        return first.e;

    }

    /**
     * 得到尾部
     * @return
     */
    public E getLast(){

        if(last==null){
            return null;
        }
        return last.e;
    }

    /**
     * 根據索引編號獲取相應的節點
     * @param index
     * @return
     */
    private Node<E> getNodeByIndex(int index){

        //判斷一下,當前的index 是在左半部分,還是在右半部分

        if(index<=(size>>2)){
            Node node=first;
            for(int i=0;i<index;i++){

                node=node.next;
            }

            return node;
        }else{
            Node node=last;
            for(int i=size-1;i>index;i--){

                node=last.previous;
            }

            return node;
        }
    }

    /**
     * 清空
     */
    public void clear(){

        //需要將以前的那些鏈接清空,方便GC 收集

        for(Node<E> node=first;node!=null;){

            Node<E> next=node.next;

            node.previous=null;
            node.e=null;
            node.next=null;

            node=next;

        }

        first=null;
        last=null;
        size=0;

        //
    }

    /**
     * 轉換成數組
     * @return
     */
    public Object[] toArray(){

        Object[] result=new Object[size];

        int i=0;

        for(Node<E>node=first;node!=null;node=node.next){

            result[i++]=node.e;
        }

        return result;

    }

    /**
     * 打印輸出
     * @return
     */
    public String toString(){

        if(size==0){
            return "[]";
        }
        StringBuilder sb=new StringBuilder();
        sb.append("[");
        for(Node<E> node=first;node!=null;node=node.next){

           String temp= node.e.toString();
           sb.append(temp+",");
        }

        sb.replace(sb.length()-1,sb.length(),"]");

        return sb.toString();

    }
}


一.二 測試自定義 MyLinkedList

package com.yjl.collection;

/**
 * package: com.yjl.collection
 * className: MyLinkedListTest
 * Description: 請輸入相應的描述
 *
 * @author : yuezl
 * @Date :2020/6/11 15:18
 */
public class MyLinkedListTest {
    public static void main(String[] args) {
        MyLinkedList<Integer> mal = new MyLinkedList<Integer>();

        System.out.println("長度:" + mal.size() + ",是否爲空:" + mal.isEmpty());
        //添加元素
        mal.add(3);

        mal.add(4);
        //索引時添加
        mal.add(0, 1);
       mal.addFirst(0);
       mal.addLast(100);
        //打印
        System.out.println("mal:" + mal.toString());

        //集合構建
        MyLinkedList<Integer> mal2 = new MyLinkedList<Integer>(mal);

        System.out.println("mal2:" + mal2.toString());
        //添加所有
        mal2.addAll(mal);

        System.out.println("新的mal2:" + mal2.toString());

        //獲取值
        Integer val = mal2.get(3);
        System.out.println("輸出值爲:" + val);

        //索引長度構造
        MyLinkedList<Integer> mal3=new MyLinkedList<Integer>();

        mal3.add(1);

        mal3.add(2);

        System.out.println("是否包含2:"+mal3.contains(2));
        System.out.println("長度1:"+mal3.toArray().length);

        //清除
        mal3.clear();

        System.out.println("長度:"+mal3.size()+",是否爲空:"+mal3.isEmpty());

    }
}

控制檯打印輸出:

有圖片

謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章