java 集群运行在一个jvm java集群数据共享_数据结构


Source From


https://learning.oreilly.com/library/view/data-structures-and/9781118771334/11_chap07.htmllearning.oreilly.com

承接


挂枝儿:Java数据结构与算法 - Stack,Queues,Dequeszhuanlan.zhihu.com

java 集群运行在一个jvm java集群数据共享_java 集群运行在一个jvm_02


接下来写点List的ADT.

真心不是完全都能吃透这种书里的内容,但多跟着写点java代码熟悉熟悉也总没错。

列表是一种能够根据数据index和position灵活进行数据查询的数据结构,在java.utl.List的接口中,定义了如下index方法:


java 集群运行在一个jvm java集群数据共享_java多个类共享的数据_03


根据这些方法,下面是一个数据结构call不同方法对应的状态:


java 集群运行在一个jvm java集群数据共享_数据结构_04


然后我们简单的实现一下这个接口:


package list;

public interface List<E> {
    int size();

    boolean isEmpty();

    E get(int i) throws IndexOutOfBoundsException;

    E set(int i,E e) throws IndexOutOfBoundsException;
    void add(int i, E e) throws IndexOutOfBoundsException;

    E remove(int i) throws IndexOutOfBoundsException;
}


用于concret 实现这个arraylist的方法显然是使用数组了,我们来实现一下:


package list;

public class ArrayList<E> implements List<E> {
    public static final int CAPACITY = 16;
    private E[] data;
    private int size = 0;
    public ArrayList() { this(CAPACITY); }
    public ArrayList(int capacity){
        this.size = capacity;
        data = (E[]) new Object[capacity];
    }

    protected void checkIndex(int i,int n) throws IndexOutOfBoundsException{
        if (i<0 || i>=n){
            throw new IndexOutOfBoundsException("Illegal index: "+ i);
        }
    }

    public int size() {return size;}

    public boolean isEmpty() {return size == 0;}

    public E get(int i) throws IndexOutOfBoundsException{
        checkIndex(i,size);
        return data[i];
    }

    public E set(int i,E e) throws IndexOutOfBoundsException{
        checkIndex(i,size);
        E temp = data[i];
        data[i] = e;
        return temp;
    }

    public void add(int i,E e) throws IndexOutOfBoundsException{
        checkIndex(i,size+1);
        if (size == data.length)
            throw new IndexOutOfBoundsException("Array is full");
        for(int k = i;i<size-1;k++){
            data[k] = data[k+1];
        }
        data[i] = e;
        size ++;
    }

    public E remove(int i) throws IndexOutOfBoundsException{
        checkIndex(i,size);
        E temp = data[i];
        for(int k=i;k<size-1;k++){
            data[k] = data[k+1];
        }
        data[size-1]=null;
        size--;
        return temp;
    }
}


这个数据结构的各个操作效率如下:


java 集群运行在一个jvm java集群数据共享_java 集群运行在一个jvm_05


可以发现有几个缺点,一个是add,remvoe的耗时为logn,第二个缺点是容量有上线,过载了就会报错,所以我们可以进一步提升,引入动态数组的概念

动态数组的实现方式还是比较简单了,先请求一块连续的内存,塞满了之后再额外请求一块内存(加大,用来存储更多的数据),不好的地方就是要把原来列表中的内容都复制一遍塞到新的表里去,示意图如下:


java 集群运行在一个jvm java集群数据共享_java多个类共享的数据_06


涉及到的resize函数代码相比应该是这么写的:


java 集群运行在一个jvm java集群数据共享_数据结构_07


一般来说,每次数组满了之后,我们的扩展策略是请求原来2倍大小的内存(书中有一段很长的证明这样子的效率会比每次塞满请求定量大小的额外内存高很多


java 集群运行在一个jvm java集群数据共享_java多个类共享的数据_08


从这里在网上,就接近了链表的概念了, index的效率终究还是不高,我们希望单独有一种 “位置”的概念表达来元素在List中的位置,假象这个“位置”(position)类的ADT应该是这样:


package list;

public interface Position<E>{
    E getElement() throws IllegalStateException;
}


根据position衍生出的positionalList的数据结构应该能够支持以下操作:


java 集群运行在一个jvm java集群数据共享_List_09


同时,与这个元列表中应该能进行如下的元素操作:


java 集群运行在一个jvm java集群数据共享_java多个类共享的数据_10


一些基础操作对应的列表状态如下:


java 集群运行在一个jvm java集群数据共享_java多个类共享的数据_11


例子聚完了,接下来动手实现一下这个PositionalList的接口把:


package list;

public interface PositionalList<E> {
    int size();

    boolean isEmpty();

    Position<E> first();

    Position<E> last();

    Position<E> before(Position<E> p) throws IllegalStateException;

    Position<E> after(Position<E> p) throws IllegalStateException;

    Position<E> addFirst(E e);

    Position<E> addLast(E e);

    Position<E> addBefore(Position<E> p,E e)
        throws IllegalStateException;

    Position<E> addAfter(Position<E> p,E e)
        throws IllegalStateException;

    E set(Position<E> p, E e) throws  IllegalStateException;

    E remove(Position<E> p) throws IllegalStateException;
}


然后我们用双向链表把这个接口给implement出来(代码看了我老久才看懂)


package list;

public class LinkedPositionalList<E> implements PositionalList<E> {

    private E element;
    private Node<E> prev;
    private Node<E> next;

    private static class Node<E> implements Position<E>{
        private E element;
        private Node<E> prev;
        private Node<E> next;

        public Node(E e,Node<E> p,Node<E> n){
            element = e;
            prev = p;
            next = n;
        }

        public E getElement() throws IllegalStateException{
            if(next == null)
                throw new IllegalStateException("Pos Not valid");
            return element;
        }

        public Node<E> getPrev(){
            return prev;
        }

        public Node<E> getNext(){
            return next;
        }

        public void setElement(E e){
            element = e;
        }
        public void setPrev(Node<E> p){
            prev = p;
        }
        public void setNext(Node<E> n){
            next = n;
        }
    }

    private Node<E> header;
    private Node<E> tailer;
    private int size = 0;

    public LinkedPositionalList(){
        header = new Node<>(null,null,null);
        tailer = new Node<>(null,header,null);
        header.setNext(tailer);
    }

    public Node<E> validate(Position<E> p) throws IllegalArgumentException{
        if (!(p instanceof Node)) throw new IllegalArgumentException("invalid P");
        Node<E> node = (Node<E>) p;
        if (node.getNext() == null)
            throw new IllegalArgumentException("p is not in list");
        return node;
    }

    private Position<E> position(Node<E> node){
        if(node == header || node == tailer)
            return null;
        return node;
    }

    public int size() {return size;}
    
    public boolean isEmpty() { return size == 0; }
    public Position<E> first(){
        return position(header.getNext());
    }

    public Position<E> last(){
        return position(tailer.getPrev());
    }

    public Position<E> before(Position<E> p) throws IllegalArgumentException{
        Node<E> node = validate(p);
        return position(node.getPrev());
    }
    public Position<E> after(Position<E> p) throws IllegalArgumentException{
        Node<E> node = validate(p);
        return position(node.getNext());
    }

    public E set(Position<E> p, E e) throws IllegalArgumentException{
        Node<E> node = validate(p);
        E answer = node.getElement();
        node.setElement(e);
        return answer;
    }

    public E remove(Position<E> p) throws IllegalArgumentException{
        Node<E> node = validate(p);
        Node<E> predecessor = node.getPrev();
        Node<E> successor = node.getNext();
        predecessor.setNext(successor);
        successor.setPrev(predecessor);
        size --;
        E answer = node.getElement();
        node.setElement(null);
        node.setNext(null);
        node.setPrev(null);
        return answer;
    }
}


这样子实现的双向链表在对两端队列以及队列当中的元素进行操作的效率都可以达到O(1),非常的香。


java 集群运行在一个jvm java集群数据共享_java 集群运行在一个jvm_12


文中接下来还实现了一个迭代器,这个实在懒得在写了,最后补一些Java collections 框架的一些内容吧。首先是collections这个框架下各种class对应的接口,以及他们的属性,底层存储方式:


java 集群运行在一个jvm java集群数据共享_ci_13


Java中的list,listiterator与文中实现的数据结构的效率对比:


java 集群运行在一个jvm java集群数据共享_java 集群运行在一个jvm_14


Collections框架中以List作为基础的元素支持的static method Collections.method()


java 集群运行在一个jvm java集群数据共享_java 集群运行在一个jvm_15


将列表转为Array


java 集群运行在一个jvm java集群数据共享_List_16


If the collection is a list, then the returned array will have its elements stored in the same order as that of the original list. Thus, if we have a useful array-based method that we want to use on a list or other type of collection, then we can do so by simply using that collection'stoArray()method to produce an array representation of that collection.

将Array变为Lists


java 集群运行在一个jvm java集群数据共享_数据结构_17


这样返回的列表是使用底层array作为基础的,所以任何在list上做的操作都会在底层array中体现。所以此方法的使用应该要当心一些。但也很有用,比如下面的代码就可以借着collections的方法来shuffle一个array


Integer[ ] arr = {1, 2, 3, 4, 5, 6, 7, 8};      // allowed by autoboxing
List<Integer> listArr = Arrays.asList(arr);
Collections.shuffle(listArr);
System.out.println(Arrays.toString(arr));

>>> [5, 2, 1, 8, 7, 3, 4, 6]


先写这些吧,啃这种书真的太痛苦了...