Handwritten Realization of One-way Link List

Keywords: Java jvm

Data structure is a necessary knowledge for a programmer. Although he has known these data structures before, he always feels that he does not know all the data structures at his fingertips. Therefore, the author intends to implement various data structures by hand in order to learn the whole picture of these data structures.

If the analysis of data structure is deep enough, then it must involve the memory level of jvm. At present, I do not have enough knowledge reserve, so I only learn to understand the implementation of these data structures from the code level.

Starting with the simplest implementation of one-way linked list, one-way linked list belongs to the most basic structure of linked list. Its definition is that linked list is a kind of physics. Storage unit Upper discontinuous and non-sequential storage structuredata elements The logical order is through the linked list Pointer Link order is implemented. A list consists of a series of nodes (each element in the list is called a node), which can be dynamically generated at run time. Each node consists of two parts: one is storage. data elements The other is to store the address of the next node. Pointer Domain. —— Introduction from Baidu Encyclopedia

                                    

Link list structure sketch

 

In my own understanding, the linked list is composed of nodes with links. Each node consists of two parts. The first part is the data stored by the node, and the second part is the pointer of the next node of the node. In such a structure, we only need to know the head node of the list, so we can get all the nodes of the whole list one by one.

The characteristics of linked list:

  1. No capacity limitation
  2. No demand for expansion
  3. Fast at both ends and slow in the middle
  4. Thread insecurity

Operation of linked list:

1. Insert a new node: assign the Next pointer of the last node of the inserting node to the Next pointer of the newly inserted node, and then point the Next pointer of the last node to the new node. The operation is completed.

                      

Insert new node schematic

2. Delete a node: The Next pointer of the last node at the location of the node to be deleted is assigned to the Next pointer of the node to be deleted.

                              

Delete node schematics

 

From the schematic diagram above, if the head and tail of the list are operated, the operation speed is the fastest. If the middle position of the list is operated, the list will be traversed.

Having said a lot of nonsense, let's start talking in code.

Code implementation of linked list:

package dataStructure;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * Created by Viking on 2019/4/7
 * Set of Implemented One-way Linked Lists
 * Only one-way traversal from the beginning to the end
 * Insertible repeatable elements
 * Implementing iterator traversal sets
 */
public class MySingleLinkList<E> {

    private transient int modCount = 0;
    private transient int size;
    private Node<E> first;
    private Node<E> last;

    public void addLast(E e){
        linkLast(e);
    }
    public E removeLast(){
        if (size==0) throw new NoSuchElementException();
       return unlinkLast(last);
    }
    public E remove(int index){
        return unlink(index);
    }
    public E getFirst(){
        return first.item;
    }
    public E getLast(){
        return last.item;
    }
    public E get(int index){
        checkElementIndex(index);
        return node(index).item;
    }

    /**
     * Add a link to the last element.
     */
    private void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    /**
     * Unlink to the last element.
     */
    private E unlinkLast(Node<E> l) {
        if (first==last&&first!=null) {
            first = null;
            last = null;
        }else if (first!=null) {
            Node<E> node = first;
            int i = 1;
            while (node.next!=null){
                if (node.next==l&&i==size-1) {
                    node.next = null;
                    last = node;
                }else  node = node.next;
                i++;
            }
        }
        size--;
        modCount++;
        return l.item;
    }

    /**
     * Cancel the node of the specified index
     */
    private E unlink(int index){
        checkElementIndex(index);
        Node<E> cursor;
        if (index==0 && index==size-1){
            cursor = first;
            first = null;
            last = null;
        }else if (index==0) {
            cursor =first;
            first = cursor.next;
        }else if (0 < index && index < size-1){
            Node<E> node = first;
            for (int i =0;i<index-1;i++) node = node.next;
            cursor = node.next;
            node.next = cursor.next;
        }else {
            cursor = last;
            last = node(index-1);
            last.next = null;
        }
        size--;
        modCount++;
        return cursor.item;
    }
    /**
     * Determines whether an element exists in the specified index.
     */
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    /**
     * Returns the element of the specified index.
     */
    private Node<E> node(int index){
        Node<E> node = first;
        for (int i =0;i<index;i++) node = node.next;
        return node;
    }
    /**
     * Constructs an IndexOutOfBoundsException detail message.
     * Of the many possible refactorings of the error handling code,
     * this "outlining" performs best with both server and client VMs.
     */
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }
    public int size(){
        return size;
    }
    public boolean isEmpty(){
        return size==0;
    }
    public String toString(){
        StringBuilder str = new StringBuilder();
        if (first==null){
            str.append("[]");
        }else {
            Node<E> next = first;
            str.append("[");
            str.append(first.item);
            while (next.next!=null){
                next = next.next;
                str.append(",");
                str.append(next.item);
            }
            str.append("]");
        }
        return str.toString();
    }

    /**
     * Nodes of linked list
     */
    private static class Node<E> {
        E item;
        Node<E> next;

        Node(E element, Node<E> next) {
            this.item = element;
            this.next = next;
        }
    }

    public Iterator<E> myIterator(){
        return new MyIterator(0);
    }
    public Iterator<E> myIterator(int index){
        return new MyIterator(index);
    }

    /**
     * Implementing iterator traversal containers
     */
    private class MyIterator implements Iterator<E>{
        private Node<E> lastReturned;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;
        private MyIterator(int index){
            next = index == size ? null : node(index);
            nextIndex = index;

        }
        @Override
        public boolean hasNext() {
            return nextIndex < size;
        }

        @Override
        public E next() {
            checkForComodification();
            if (!hasNext())
                throw new NoSuchElementException();

            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.item;
        }

        @Override
        public void remove() {
            checkForComodification();
            if (lastReturned == null)
                throw new IllegalStateException();

            Node<E> lastNext = lastReturned.next;
            unlink(nextIndex-1);
            if (next == lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = null;
            expectedModCount++;
        }

        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (modCount == expectedModCount && nextIndex < size) {
                action.accept(next.item);
                lastReturned = next;
                next = next.next;
                nextIndex++;
            }
            checkForComodification();
        }
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
}

I refer to the source code of LinkedList. LinkedList is a two-way linked list.

Write a test class:

import dataStructure.MySingleLinkList;

import java.util.*;

/**
 * Created by Viking on 2019/4/7
 */
public class TestSList {
    public static void main(String[] args) {
        MySingleLinkList<String> sList = new MySingleLinkList<>();
        System.out.println(sList.size());
        System.out.println(sList);
        sList.addLast("Hello!");
        sList.addLast("My");
        sList.addLast("Link");
        sList.addLast("List");
        sList.addLast("last");
        sList.addLast("same");
        sList.addLast("same");
        System.out.println(sList.size());
        System.out.println(sList.isEmpty());
        System.out.println(sList);
        System.out.println("index of 0:"+sList.get(0));
        System.out.println("index of 1:"+sList.get(1));
        System.out.println("index of 2:"+sList.get(2));
        System.out.println("index of 3:"+sList.get(3));
        System.out.println("index of 4:"+sList.get(4));
        System.out.println("getFirst:"+sList.getFirst());
        System.out.println("getLast:"+sList.getLast());
        System.out.println(sList);
        System.out.println(sList.removeLast());
        System.out.println("After remove 1 element:size="+sList.size());
        System.out.println(sList.removeLast());
        System.out.println("After remove 2 element:size="+sList.size());
        System.out.println(sList.removeLast());
        System.out.println("After remove 3 element:size="+sList.size());
        System.out.println(sList.removeLast());
        System.out.println("After remove 4 element:size="+sList.size());
        System.out.println(sList.removeLast());
        System.out.println("After remove 5 element:size="+sList.size());
        System.out.println(sList);
        sList.addLast("iterator");
        sList.addLast("is");
        sList.addLast("cool");
        Iterator<String> stringIterator = sList.myIterator();
//        while (stringIterator.hasNext()){
//            System.out.println(stringIterator.next());
//        }
//        System.out.println(sList);

        int i =0;
        while (stringIterator.hasNext()){//An iterator object can only perform one iteration
            System.out.println(stringIterator.next());
            if (i<3) stringIterator.remove();
            i++;
//            if (sList.size()==1) break;
        }
        System.out.println(sList);
    }
}

Test results:

0
[]
7
false
[Hello!,My,Link,List,last,same,same]
index of 0:Hello!
index of 1:My
index of 2:Link
index of 3:List
index of 4:last
getFirst:Hello!
getLast:same
[Hello!,My,Link,List,last,same,same]
same
After remove 1 element:size=6
same
After remove 2 element:size=5
last
After remove 3 element:size=4
List
After remove 4 element:size=3
Link
After remove 5 element:size=2
[Hello!,My]
Hello!
My
iterator
is
cool
[is,cool]

 

Posted by jayshadow on Mon, 22 Apr 2019 19:45:33 -0700