走进JDK------AbstractList

Posted alimayun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了走进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都是他的子类,所以后面学起来就能串起来了,毕竟用了很多父类的功能。

 

以上是关于走进JDK------AbstractList的主要内容,如果未能解决你的问题,请参考以下文章

PHP走进 PHP 第三课 基础语法

PHP走进 PHP 第三课 基础语法

当您走进地理围栏时,如何为简单的信息敬酒?

Golang✔️走进 Go 语言✔️ 第八课 函数

Golang✔️走进 Go 语言✔️ 第八课 函数

Golang✔️走进 Go 语言✔️ 第六课 循环语句