zoukankan      html  css  js  c++  java
  • 走进JDK(五)------AbstractList

    接下来的一段时间重点介绍java.util这个包中的内容,这个包厉害了,包含了collection与map,提供了集合、队列、映射等实现。一张图了解java中的集合类:

    AbstractList

    一、list简介

    list是啥?为啥会有list的存在呢?java中的数组相信大家都是非常熟悉的,可以存放多个数据,但是数组有一个缺点,就是数组在创建之后,长度就不可更改(但是针对于数组的元素可以更改),若你需要在后续过程中往数组中添加数据,那麻烦了,不支持。

    list在java中是collection(集合)的子接口,运行过程中可以增加元素或是减少元素。List里存放的对象是有序的(有序不是指按照元素的大小排列,是按照元素添加顺序为维度的,例如第一个被添加的元素,get的时候就第一个被拿出来),同时也是可以重复的。

    二、AbstractList类定义

    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
    
    protected AbstractList() {}

    成员变量:

    //代表修改次数
    protected transient int modCount = 0;

    三、主要方法

    1、add()、addAll()

    //把一个元素加进list
    public boolean add(E e) {
            //当然是调用指定index的方法,传入size,还有元素即可,假设一共5个元素,那么下标5一定是空的喽
            add(size(), e);
            return true;
    }
    //由各子类实现
    public void add(int index, E element) {
            throw new UnsupportedOperationException();
    }
    public boolean addAll(int index, Collection<? extends E> c) {
            //校验index是否<0或>list.size()
            rangeCheckForAdd(index);
            boolean modified = false;
            for (E e : c) {
                //循环添加元素
                add(index++, e);
                modified = true;
            }
            return modified;
    } 

    2、get()、set()、remove()

    //都是由子类实现
    //获取对应索引位的元素
    abstract public E get(int index);
    //更改给定索引位的元素
    public E set(int index, E element) {
            throw new UnsupportedOperationException();
    }
    //根据某个索引删除该索引位的元素
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

    3、indexOf()、lastIndexOf()

    public int indexOf(Object o) {
            //使用listIterator迭代器
            ListIterator<E> it = listIterator();
            if (o==null) {
                while (it.hasNext())
                    //在执行next()方法时,取的是当前对象,并且cursor+1,可以看后面内部类的介绍
                    if (it.next()==null)
                        //由于cursor+1,所以要取前一个索引值
                        return it.previousIndex();
            } else {
                while (it.hasNext())
                    //如果元素不为null的话,使用equals()进行比较。因为null.equals()会报空指针
                    if (o.equals(it.next()))
                        return it.previousIndex();
            }
            //若list没有此元素,则返回-1
            return -1;
    }
    //倒着循环list,就ok了
    public int lastIndexOf(Object o) {
            ListIterator<E> it = listIterator(size());
            if (o==null) {
                while (it.hasPrevious())
                    if (it.previous()==null)
                        return it.nextIndex();
            } else {
                while (it.hasPrevious())
                    if (o.equals(it.previous()))
                        return it.nextIndex();
            }
            return -1;
    }

    4、clear()

    //清空整个list
    public void clear() {
            removeRange(0, size());
    }
    //将fromIndex到toIndex范围内的元素全干掉
    protected void removeRange(int fromIndex, int toIndex) {
            ListIterator<E> it = listIterator(fromIndex);
            for (int i=0, n=toIndex-fromIndex; i<n; i++) {
                it.next();
                it.remove();
            }
    }

    5、subList()

    public List<E> subList(int fromIndex, int toIndex) {
            return (this instanceof RandomAccess ?
                    new RandomAccessSubList<>(this, fromIndex, toIndex) :
                    new SubList<>(this, fromIndex, toIndex));
        }

    6、equals()、hashCode()

    public boolean equals(Object o) {
            if (o == this)
                return true;
            if (!(o instanceof List))
                return false;
    
            ListIterator<E> e1 = listIterator();
            ListIterator<?> e2 = ((List<?>) o).listIterator();
            while (e1.hasNext() && e2.hasNext()) {
                E o1 = e1.next();
                Object o2 = e2.next();
                //比较的逻辑就在这了,如果都为null,就相等,否则就equals比较
                if (!(o1==null ? o2==null : o1.equals(o2)))
                    return false;
            }
            //这地方也很好理解,比如两个list,一个5个长度,一个6个长度,并且前5个元素都相等,前面的玄幻已经干完5个元素的比较了。那就判断是否还有下一个,就可以得出他们长度是否相等了
            return !(e1.hasNext() || e2.hasNext());
        }    
    public int hashCode() {
            int hashCode = 1;
            for (E e : this)
                //为啥乘以31?31是质子数中一个“不大不小”的存在,如果你使用的是一个如2的较小质数,那么得出的乘积会在一个很小的范围,很容易造成哈希值的冲突。而如果选择一个100以上的质数,得出的哈希值会超出int的最大范围,这两种都不合适。
    而如果对超过 50,000 个英文单词(由两个不同版本的 Unix 字典合并而成)进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个(国外大神做的测试),那么这几个数就被作为生成hashCode值得备选乘数了。
    hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); return hashCode; }

    四、迭代器Iterator

    迭代器是什么东西呢?为啥循环list的时候需要用到它?其实原因也很简单,迭代器其实是一种设计模式,在java中,list的实现有很多种,例如数组列表(ArrayList)、链表结构的(LinkedList)等等。那么循环这些不同数据结构的list就要命了,因此呢,使用Iterator封装循环获取list的方法,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。

    1、iterator()、listIterator()

    上面的代码中经常出现listIterator()方法

        //Itr以及ListItr是两个内部类
        public Iterator<E> iterator() {
            return new Itr();
        }
    
        public ListIterator<E> listIterator() {
            return listIterator(0);
        }
        public ListIterator<E> listIterator(final int index) {
            //检查索引是否<0或>list.size
            rangeCheckForAdd(index);
    
            return new ListItr(index);
        }

    2、Itr

    private class Itr implements Iterator<E> {
            //在调用next()之后,cursor+1(因为最终cursor与size做比较的,cursor=index+1)随后返回的下标
            int cursor = 0;
            //调用next、previous,都会更新该下标值,当调用remove元素,就会重置为-1,代表最后返回的元素下标
            int lastRet = -1;
            ////将Abstract修改次数赋值给预期次数
            int expectedModCount = modCount;
            //看到这里就好理解上面了,cursor比较的是size。例如list有5个元素,size为5,index为0-4
            public boolean hasNext() {
                return cursor != size();
            }
    
            public E next() {
                //检查修改次数
                checkForComodification();
                try {
                    int i = cursor;
                    //获取当前元素
                    E next = get(i);
                    //设置为当前索引
                    lastRet = i;
                    //获取到当前元素之后,cursor=cursor+1
                    cursor = i + 1;
                    return next;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    //内部类调用外部类的方法,类名.this.xxx(),删除的是next()调用时的元素
                    AbstractList.this.remove(lastRet);
                    if (lastRet < cursor)
                        //删除了一个元素,cursor自然要-1
                        cursor--;
                    //将lastRet重置为-1
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException e) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }

    3、ListItr

    //这玩意扩展了Itr,可以倒着循环
    private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                //这里面的cursor就等于index。跟Itr不同
                cursor = index;
            }
    
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            public E previous() {
                checkForComodification();
                try { 
                    //取前一个元素,这里的cursor跟index一致。当前index-1
                    int i = cursor - 1;
                    E previous = get(i);
                    lastRet = cursor = i;
                    return previous;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
            //其实就是获取当前元素的下标
            public int nextIndex() {
                return cursor;
            }
            //获取前一个元素的下标
            public int previousIndex() {
                return cursor-1;
            }
    
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    AbstractList.this.set(lastRet, e);
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            public void add(E e) {
                checkForComodification();
    
                try {
                    int i = cursor;
                    AbstractList.this.add(i, e);
                    //添加元素也将lastRet设为-1
                    lastRet = -1;
                    cursor = i + 1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }

    为啥先介绍AbstractList?因为后面的重点ArrayList以及LinkedList都是他的子类,所以后面学起来就能串起来了,毕竟用了很多父类的功能。

  • 相关阅读:
    微信聊天框测试思路
    巧用&&和|| 让逻辑代码更简洁,逼格看起来更高一点(玩笑脸)
    获取URL中的参数
    解决移动端点击闪烁问题
    npm安装依赖包 --save-dev 和 --save; package.json的devDependencies和dependencies 的区别!
    vue-cli 3配置接口代理
    js小方法积累,将一个数组按照n个一份,分成若干数组
    web前端识别文字转语音
    html 锚点
    ES6 必须要用的数组Filter() 方法,不要再自己循环遍历了!!!
  • 原文地址:https://www.cnblogs.com/alimayun/p/10703285.html
Copyright © 2011-2022 走看看