表的学习(Java实现)

表的学习笔记


主要是ArrayList和LinkedList的手动实现

3.3 Java 集合中的表

概念

  • 在Java中包含着一些普通数据结构的实现,该部分称为CollectionAPI,位于java.util包中

3.3.1 Collection接口

Collection位于java.util包中,实现了Iterator接口,实现Iterator接口的可以拥有增强for循环,下面是Collection接口提供的一些重要方法

public interface Collection<T> extends Iterable<T>{
    int size();
    boolean isEmpty();
    void clear();
    boolean contains(T t);
    boolean add(T t);
    boolean remove(T t);
    java.util.Iterator<T> iterator();
}

3.3.2 Iterator接口

实现Iterator接口的集合必须提供一个称为iterator的方法,该方法返回一个Iterator类型的对象。下面是Iterator接口的定义

public interface Iterator<T>{
    boolean hasNext();
    T next();
    void remove();
}

Iterator接口实现的思路是通过iterator方法,每个集合均可以创建并且返回给客户一个实现Iterator接口的对象,并将当前位置的概念在对象内部存储起来。

每次对next的调用都给出集合的下一项,第一次调用给出第一项,第n次调用第n项。使用hasNext()告诉是否还有下一项。

Iterator接口还包含remove()的方法,该方法可以删除由next返回的最新项。它的优点在于,Collection的remove方法必须首先找出被删除的项

当直接使用Iterator,而不是使用增强for循环时的基本法则:如果对正在被迭代的集合上进行结构上的改变,即对该集合使用add、remove或者clear方法,那么迭代器就不再合法。但是在使用Iterator自己的remove方法时,那么他仍然是合法的。

3.3.3 List接口、ArrayList类和LinkedList类

下面是List接口,他指定listIterator方法作为迭代器

public interface List<T> extends Collection<T>{
    T get(int index);
    T set(int index,T t);
    void add(int index,T t);
    void remove(int index);
    ListIterator<T> listIterator(int pos);
}

List ADT有两种流行的实现方式。ArrayList类提供了List ADT的一种可增长的数组实现。

使用ArrayList的优点是get和set花费常数时间,缺点是新项加入和现有项删除花费O2时间,除非变动是在末端实现的。LinkedList则提供了List ADT的双链表实现。

使用LinkedList的优点是新项加入和现有项删除开销很小,缺点是LinkedList不容易做索引,对get的调用花费时间很长,除非调用非常接近表的端点。

对下面求List和的算法来说,对ArrayList是O,对LinkedList是O2,但是如果使用增强for循环,那么他们两个都是O2。因为迭代器可以有效的从一项推进到下一项

public int sum(List<Integer> lst){
    int total=0;
    for(int i=0;i<N;i++){
        total+=lst.get(i);
    }
    return total;
}

3.3.4 例子:删除LinkedList中的偶数项

方法一

public void method1(List<Integer> list){
    int i=0;
    while(i<list.size()){
        if(lst.get(i)%2==0){
            list.remove(i);
        }
    }
}

方法二

public void method2(List<Integer> list){
    for(Integer i:list){
        if(i%2==0){
            list.remove(i);
        }
    }
}

方法三

public void method3(List<Integer> list){
    Iterator<Integer> iter=list.iterator();
    while(iter.hasNext()){
        if(iter.next()%2==0){
            iter.remove();
        }
    }
}

分析:
方法1,是最基本的方法,但是对于LinkedList来说,get方法很低效,remove方法也不高效,算法复杂度O2
方法2,想用迭代器规避get的低效,但是改变了结构,会使迭代器失效,方法二会直接报错。
方法3,转化为Iterator,使用迭代器保证了高效访问与删除,因为相当于直接知道位置了。算法复杂度O

3.4 ArrayList的实现

下面是技术要求

  • MyArrayList将保持基础数组,数组的容量以及存储在MyArrayList中的当前项数

  • MyArrayList将将提供一种机制改变便基础数组的容量,获得一个新数组,把老数组数据copy到新数组来改变数组容量,允许JVM回收老数组

  • MyArrayList将提供get和set实现

  • MyArrayList将提供基本的例程,如size isEmpty clear,还提供remove 以及两种add,如果数组大小和容量相同,这两个add例程都将增加容量。

  • MyArrayList将提供一个实现Iterator接口的类。这个类将存储迭代序列中的下一项的下标,并且提供next hasNext remove等方法的实现,MyArrayList的迭代器方法直接返回实现Iterator接口的该类的新构造的实例。

import java.util.Iterator;

public class MyArrayList<T> implements Iterable<T> {
    private static final int DEFAULT_CAPACITY=10;
    private int theSize;
    private T[] theItems;

    /**
     * 初始化ArrayList
     */
    public MyArrayList(){
        clear();
    }
    //大小
    public int size(){
        return theSize;
    }
    //清空
    public void clear(){
        theSize=0;
        ensureCapacity(DEFAULT_CAPACITY);
    }

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

    public void ensureCapacity(int newCapacity){
        if(newCapacity<theSize){
            return;
        }
        T[] old=theItems;
        theItems=(T[])new Object[newCapacity];
        for(int i=0;i<size();i++){
            theItems[i]=old[i];
        }
    }

    public T get(int index){
        if(index>size()||index<0){
            throw new ArrayIndexOutOfBoundsException();
        }
        return theItems[index];
    }

    public T set(int index,T newValue){
        if(index<0||index>size()){
            throw new ArrayIndexOutOfBoundsException();
        }
        T old=theItems[index];
        theItems[index]=newValue;
        return old;
    }


    public T remove(int index){
        T item=theItems[index];
        for(int i=index;i<size()-1;i++){
            theItems[i]=theItems[i+1];
        }
        theSize--;
        return item;
    }

    public void add(T t){
        add(size(),t);
    }
    public void add(int index,T t){
        if(theItems.length==theSize){
            ensureCapacity(theSize*2+1);
        }


        for(int i=theSize;i>index;i--){
            theItems[i]=theItems[i-1];
        }
        theItems[index]=t;
        theSize++;
    }

    public java.util.Iterator<T> iterator(){
        return new ArrayListIterator<T>(this);
    }

    private static class ArrayListIterator<T> implements java.util.Iterator<T>{
        private int current=0;
        private MyArrayList<T> list;

        public ArrayListIterator(MyArrayList<T> list) {
            this.list=list;
        }

        public boolean hasNext(){
            return current<list.size();
        }

        public T next(){
            if(!hasNext()){
                throw  new java.util.NoSuchElementException();
            }
            return list.theItems[current++];
        }

        public void remove(){
            list.remove(--current);
        }
    }
}

3.5 LinkedList的实现

下面是技术要求

  • 提供MyLinkedList类本身,它包含到两端的链、表的大小以及一些方法

  • Node类,它可能是一个私有的嵌套类。一个节点包含数据以及到前后的链,还有一些适当的构造方法

  • LinkedListIterator类,该类抽象了位置的概念,是一个私有类,并实现接口Iterator.它提供了next、hasNext和remove实现。

为了排除一些特殊情形,在表的终端创建一个额外的节点来表示终端标记,这些节点称为标记节点,在前端的节点也叫做头节点(header node),而在末端的节点有时候也叫尾节点(tail node)

    private static class Node<T>{
        public T data;
        public Node<T> prevNode;
        public Node<T> nextNode;
        public Node(T data,Node<T> prevNode,Node<T> nextNode){
            this.data=data;
            this.prevNode=prevNode;
            this.nextNode=nextNode;
        }
    }

下面是基本骨架

public class MyLinkedList<T> implements Iterable<T> {
    private int theSize;
    private int modCount=0;
    private Node<T> beginMarker;
    private Node<T> endMarker;
    private static class Node<T>{
        public T data;
        public Node<T> prevNode;
        public Node<T> nextNode;
        public Node(T data,Node<T> prevNode,Node<T> nextNode){
            this.data=data;
            this.prevNode=prevNode;
            this.nextNode=nextNode;
        }
    }

    public MyLinkedList(){
        clear();
    }
    public void clear(){

    }
    public int size(){
        return theSize;
    }
    public boolean isEmpty(){
        return size()==0;
    }
    public boolean add(T newNode){
        //Todo
    }
    public void add(int index,T newNode){
        //Todo
    }
    public T get(int index){
        //Todo
    }
    public T set(int index,T newNode){
        //Todo
    }
    public T remove(int index){
        //Todo
    }
    public java.util.Iterator<T> iterator(){
        return new LinkedListIterator();
    }
    private class LinkedListIterator implements java.util.Iterator<T>{
        //Todo
    }

    ...

}

各部分实现

  • 链表初始化

首先是双链表的初始化,使用clear来实现,让头节点为null,尾节点的前一个结点为头节点,后一个节点为null,数值也为null,然后让头节点的下一个节点为尾节点,使当前大小theSize=0,修改次数加1

    public void clear(){
        beginMarker=new Node<>(null,null,null);
        endMarker=new Node<>(null,beginMarker,null);
        beginMarker.nextNode=endMarker;
        theSize=0;
        modCount++;
    }
  • 节点添加

然后是双链表的节点添加。为了方便起见,定义一个私有函数addBefore(Node node,T value)实现在某个位置前面添加节点,为了得到这个位置原来的节点,再实现一个获取的函数getNode(int index)

    //添加节点
    public boolean add(T newNode){
        add(size(),newNode);
        return true;
    }
    public void add(int index,T newNode){
        addBefore(getNode(index),newNode);
    }
    /**
     *
     * @param node 这个Node是插入位置的原来的Node,值是新的值
     * @param value
     */
    private void addBefore(Node<T> node,T value){
        Node<T> newNode=new Node<>(value,node.prevNode,node.nextNode);
        newNode.prevNode.nextNode=newNode;
        node.prevNode=newNode;
        theSize++;
        modCount++;

    }

    private Node<T> getNode(int index){
        Node<T> node;
        if(index<0||index>size()-1){
            throw new IndexOutOfBoundsException();
        }
        if(index<size()/2){
            node=beginMarker.nextNode;
            for(int i=0;i<index;i++){
                node=node.nextNode;
            }
        }else {
            node=endMarker;
            for(int i=size();i>index;i--){
                node=node.prevNode;
            }
        }
        return node;
    }
  • 节点删除

节点删除就是断开那个节点的键,首先就要获取节点getNode,然后操作

    public T remove(int index){
        Node<T> node=getNode(index);
        node.nextNode.prevNode=node.prevNode;
        node.prevNode.nextNode=node.nextNode;
        theSize--;
        modCount++;
        return node.data;
    }
  • 获取节点的值和设置节点的值

获取节点对象后对其修改即可

    public T get(int index){
        return getNode(index).data;
    }

    public T set(int index,T newValue){
        Node<T> node=getNode(index);
        T oldValue=node.data;
        node.data=newValue;
        return oldValue;
    }
  • 迭代器的实现

迭代器内部需要实现next hasNext 和remove方法.exceptedModCount是为了防止在使用迭代器时改变链表结构而做的标志

    private T remove(Node<T> node){
        node.nextNode.prevNode=node.prevNode;
        node.prevNode.nextNode=node.nextNode;
        theSize--;
        modCount++;
        return node.data;
    }

    public java.util.Iterator<T> iterator(){
        return new LinkedListIterator();
    }

    private class LinkedListIterator implements java.util.Iterator<T>{

        private Node<T> current=beginMarker.nextNode;
        private int exceptedModCount=modCount;
        private boolean canRemove=false;

        public boolean hasNext() {
            return current!=endMarker;
        }

        public T next() {
            if(modCount!=exceptedModCount){
                throw new ConcurrentModificationException();
            }
            if(!hasNext()){
                throw new NoSuchElementException();
            }
            T value=current.data;
            current=current.nextNode;
            canRemove=true;
            return value;
        }

        public void remove() {
            if(modCount!=exceptedModCount){
                throw new ConcurrentModificationException();
            }
            if(!canRemove){
                throw new IllegalStateException();
            }
            MyLinkedList.this.remove(current.prevNode);
            exceptedModCount++;
            canRemove=false;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值