java--19--List及其实现类与子接口

2 LinkList实现List接口,可以存储null至其中。有两个指针,一个指向第一个元素,一个指向最后的元素。

/**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

每一个元素的结构为一个指向前和指向后的指针。类似于数据结构中的双链表。

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
	
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
头插法:
/**
     * Links e as first element.
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;//插入之前是空,头尾指针均指向该节点
        else
            f.prev = newNode;//更新之前的头指针指向前的位置
        size++;
        modCount++;
    }
尾插法:
    /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;//插入之前为空,头尾指针均指向它
        else
            l.next = newNode;//不要空,更新之前的尾指针的尾指向
        size++;
        modCount++;
    }

自己实现的LinkedList

/**
 * @Author: ShipTang
 * @Description: LinkedList重写
 * @Date: 2021/8/9 16:04
 */
public class LinkedListRewrite<E> {

    /**
     * 设置空节点,不用担心头节点和尾节点出现空指针
     */
    private Node<E> emptyNode = new Node<>(null);

    /**
     * 头指针
     */
    private Node<E> headNode = emptyNode;

    /**
     * 尾指针
     */
    private Node<E> endNode = emptyNode;

    /**
     * 链表节点个数
     */
    private Integer size = 0;

    public void add(E e) {
        Node<E> node = new Node<>(e);
        this.endNode.nextNode = node;
        node.preNode = this.endNode;
        this.endNode = node;
        size++;
    }

    public void add(int index, E e) {
        if (index < 0 && index >= this.size) {
            throw new ArrayIndexOutOfBoundsException("越过链表长度");
        }

        //获取被替换的节点
        Node<E> indexNode = this.getNode(index);
        Node<E> preNode = indexNode.preNode;
        Node<E> newNode = new Node<>(e);

        preNode.nextNode = newNode;
        newNode.preNode = preNode;

        newNode.nextNode = indexNode;
        indexNode.preNode = newNode;
    }

    private Node<E> getNode(int index) {
        if (index < (this.size >> 1)) {
            //从头开始
            Node<E> node = this.headNode.nextNode;
            for (int i = 0; i < index; i++) {
                node = node.nextNode;
            }
            return node;
        } else {
            //从尾开始
            Node<E> node = this.endNode.preNode;
            for (int i = size - 1; index < i; i--) {
                node = node.preNode;
            }
            return node;
        }
    }

    public E get(int index) {
        if (index < 0 && index >= this.size) {
            throw new ArrayIndexOutOfBoundsException("越过链表长度");
        }
        Node<E> node = this.getNode(index);
        return node.e;
    }

    public E remove(int index) {
        if (index < 0 && index >= this.size) {
            throw new ArrayIndexOutOfBoundsException("越过链表长度");
        }
        Node<E> currNode = this.getNode(index);
        Node<E> preNode = currNode.preNode;
        Node<E> nextNode = currNode.nextNode;
        preNode.nextNode = nextNode;
        nextNode.preNode = preNode;

        //断开
        currNode.preNode = null;
        currNode.nextNode = null;
        return currNode.e;
    }

    public void printList() {
        //从头到尾
        Node<E> node1 = this.headNode.nextNode;
        System.out.println("---------------从头到尾--------------");
        while (node1 != this.endNode) {
            System.out.print("->" + node1.e);
            node1 = node1.nextNode;
        }

        //从尾到头
        Node<E> node2 = this.endNode.preNode;
        System.out.println();
        System.out.println("---------------从尾到头--------------");
        while (node2 != this.headNode) {
            System.out.print("->" + node2.e);
            node2 = node2.preNode;
        }
    }

    private static class Node<E> {

        private E e;

        private Node<E> preNode;

        private Node<E> nextNode;

        Node(E e) {
            this.e = e;
        }
    }
}

测试方法

    @Test
    public void add() {
        LinkedListRewrite<Integer> linkedListRewrite = new LinkedListRewrite<>();
        for (int i = 0; i < 16; i++) {
            linkedListRewrite.add(i);
        }
        linkedListRewrite.printList();
    }

    @Test
    public void add1() {
        LinkedListRewrite<Integer> linkedListRewrite = new LinkedListRewrite<>();
        for (int i = 0; i < 16; i++) {
            linkedListRewrite.add(i);
        }
        linkedListRewrite.add(14, 1414);
        linkedListRewrite.printList();
    }

    @Test
    public void get() {
        LinkedListRewrite<Integer> linkedListRewrite = new LinkedListRewrite<>();
        for (int i = 0; i < 16; i++) {
            linkedListRewrite.add(i);
        }
        for (int i = 0; i < 16; i++) {
            System.out.println(linkedListRewrite.get(i));
        }
    }

    @Test
    public void remove() {
        LinkedListRewrite<Integer> linkedListRewrite = new LinkedListRewrite<>();
        for (int i = 0; i < 16; i++) {
            linkedListRewrite.add(i);
        }
        for (int i = 0; i < 16; i++) {
            System.out.println(linkedListRewrite.remove(0));
        }
    }
  • ArrayList
  • 扩容方法
//自增ArrayList空间大小
//参数minCapacity为增加的最小大小
private void grow(int minCapacity) {
        int oldCapacity = elementData.length;//目前的大小
        int newCapacity = oldCapacity + (oldCapacity >> 1);//增加目前的一半,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;//增加后小于需要的大小,直接增大到参数传递的值
        if (newCapacity - MAX_ARRAY_SIZE > 0)//
            newCapacity = hugeCapacity(minCapacity);//增大到最大
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//元素复制
}

每 次 扩 容 的 大 小 为 当 前 长 度 + 当 前 长 度 右 移 一 位 得 到 的 数 。 \color{#FF0000}{每次扩容的大小为当前长度+当前长度右移一位得到的数。} +
n e w A r r a y L i s t 数 组 时 大 小 为 一 个 空 数 组 , 执 行 添 加 任 务 的 时 候 , 初 始 化 大 小 为 10. \color{#FF0000}{new ArrayList数组时大小为一个空数组,执行添加任务的时候,初始化大小为10.} newArrayList10.

ArrayList的坑:

    @Test
    public void test02(){
        String[] arrays = {"1", "2", "3"};
        List<String> list = new ArrayList<>(Arrays.asList(arrays));
        List<String> subList = list.subList(0, 1);
        subList.set(0, "10");
        System.out.println(JSON.toJSONString(list));
        System.out.println(JSON.toJSONString(subList));
    }

输出:

["10","2","3"]
["10"]

修改了是子集,结果影响到了父集合。解决方案为subList返回的结果集套一层ArrayList即可

    @Test
    public void test02(){
        String[] arrays = {"1", "2", "3"};
        List<String> list = new ArrayList<>(Arrays.asList(arrays));
        List<String> subList = new ArrayList<>(list.subList(0, 1));
        subList.set(0, "10");
        System.out.println(JSON.toJSONString(list));
        System.out.println(JSON.toJSONString(subList));
    }

参考:https://mp.weixin.qq.com/s/1lHrVnMckr5NuNThVJkWMQ

在迭代中移除元素,如下代码会陷入死循环。

        List<Integer> lstReviewTaskIds = new ArrayList<>();
        lstReviewTaskIds.add(1);
        lstReviewTaskIds.add(2);
        lstReviewTaskIds.add(3);
        lstReviewTaskIds.add(4);
        lstReviewTaskIds.add(5);

        List<Integer> checkedReviewTaskIds = new ArrayList<>();
        Iterator<Integer> idsIterator = lstReviewTaskIds.iterator();
        while (idsIterator.hasNext()) {//如果checkedReviewTaskIds为空,那么idsIterator.hasNext()总是为true
            for (Integer checkedReviewTaskId : checkedReviewTaskIds) {
                if (Objects.equals(checkedReviewTaskId, idsIterator.next())) {
                    idsIterator.remove();
                }
            }
        }
        System.out.println(JSON.toJSONString(lstReviewTaskIds));

查阅源码可知,在next方法中,会将指向下一个的指针+1,如果不执行,hasNext判断逻辑为是否等于list的size,这样就一直返回true.正确的方式是在for循环开始前获取id,Integer id=idsIterator.next();或者直接使用ArrayList提供的removeAll方法。

自己实现ArrayList

public class ArrayListReview<E> {

    /**
     * 容器数组
     */
    private Object[] elementData;

    /**
     * 当前容器元素个数
     */
    private Integer size = 0;

    public ArrayListReview() {
        this(8);
    }

    public ArrayListReview(int capacity) {
        this.elementData = new Object[capacity];
    }

    public void add(E e) {
        if (size >= elementData.length) {
            //数组容器已满,需要扩容数组
            this.grow();
        }
        elementData[this.size++] = e;
    }

    /**
     * 扩容数组
     */
    private void grow() {
        int newLength = elementData.length + elementData.length >> 1;
        this.elementData = Arrays.copyOf(this.elementData, newLength);
    }

    public void add(int index, E e) {
        if (index >= this.size) {
            throw new ArrayIndexOutOfBoundsException("数组下标越界");
        }
        if (size >= elementData.length) {
            //扩容数据
            this.grow();
        }
        int moveNum = (this.size - 1) - index + 1;
        System.arraycopy(this.elementData, index, this.elementData, index + 1, moveNum);
        this.elementData[index] = e;
        size++;
    }

    @SuppressWarnings("unchecked")
    public E remove(int index) {
        E e = (E) this.elementData[index];

        //向前迁移数据
        System.arraycopy(this.elementData, index + 1, this.elementData, index, (this.size - 1) - index);
        elementData[--size] = null;
        return e;
    }

    public void printList() {
        for (Object e : elementData) {
            System.out.println(e);
        }
    }
}

测试代码

public class ArrayListReviewTest {

    @Test
    public void add() {
        ArrayListReview<Integer> arrayListReview = new ArrayListReview<Integer>();
        for (int i = 0; i < 17; i++) {
            arrayListReview.add(i);
        }
        arrayListReview.printList();
    }

    @Test
    public void add1() {
        ArrayListReview<Integer> arrayListReview = new ArrayListReview<Integer>();
        for (int i = 0; i < 4; i++) {
            arrayListReview.add(i);
        }
        arrayListReview.add(3, 33);
    }

    @Test
    public void remove() {
        ArrayListReview<Integer> arrayListReview = new ArrayListReview<Integer>();
        for (int i = 0; i < 4; i++) {
            arrayListReview.add(i);
        }
        arrayListReview.remove(1);
    }
}
  • CopyOnWriteArrayList
    初始化分配的数组空间大小为0
private transient volatile Object[] array;

存放的数组用volatile修饰,保证获取数组指针指向为最新的指向。

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

先上锁-》复制数据到之前数组+1的新数组中-》释放锁。

2022年2月16日更新
在遍历集合的时候同时修改容器

    /**
     * 没问题
     */
    @Test
    public void test01(){
        List<String> list=new ArrayList<>();
        list.add("0");
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");

        for (int i = 0; i < list.size(); i++) {
            if("2".equals(list.get(i))){
                list.remove(4);
            }
        }
        System.out.println(JSON.toJSONString(list));
    }
    /**
     * 使用迭代器-用迭代器中的remove,不会有问题
     */
    @Test
    public void test02(){
        List<String> list=new ArrayList<>();
        list.add("0");
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String s = iterator.next();
            if(s.equals("2")){
                iterator.remove();
            }
        }
        System.out.println(JSON.toJSONString(list));
    }
    /**
     * 使用迭代器-modCount和expectModCount不一致导致的异常
     * java.util.ConcurrentModificationException
     * 	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
     * 	at java.util.ArrayList$Itr.next(ArrayList.java:851)
     * 	at com.txp.owner.basic.record.test202202.TestMain20220216.test03(TestMain20220216.java:73)
     */
    @Test
    public void test03(){
        List<String> list=new ArrayList<>();
        list.add("0");
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String s = iterator.next();
            if(s.equals("2")){
                list.remove(4);
            }
        }
        System.out.println(JSON.toJSONString(list));
    }
    /**
     * 本质用的迭代器
     * java.util.ConcurrentModificationException
     * 	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
     * 	at java.util.ArrayList$Itr.next(ArrayList.java:851)
     * 	at com.txp.owner.basic.record.test202202.TestMain20220216.test02(TestMain20220216.java:41)
     */
    @Test
    public void test04(){
        List<String> list=new ArrayList<>();
        list.add("0");
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        for (String s : list) {
            if("2".equals(s)){
                list.remove(4);
            }
        }
        System.out.println(JSON.toJSONString(list));
    }

4 Vector
4.1 类说明:实现List接口,继承AbstractList,类中用数组的形式存储数据,Vector中的方法大部分都是线程安全。

The Vector class implements a growable array of objects. Like an array, it contains components that can be accessed using an integer index. However, the size of a Vector can grow or shrink as needed to accommodate adding and removing items after the code Vector has been created.
4.2 重要方法:
迭代遍历输出元素:

ListIterator e2 = ((List) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值