前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java进阶|Vector源码分析和理解

java进阶|Vector源码分析和理解

作者头像
码农王同学
发布2020-04-08 16:19:43
3150
发布2020-04-08 16:19:43
举报
文章被收录于专栏:后端Coder后端Coder

这一篇文章算是从java基础性文章结束到进阶的一个过渡,虽然自己从未使用过Vector这样的容器进行数据的增删改查操作,但还是按照一贯的思路进行分析一下它的源码。

先简单看下它的结构图,然后看下提供的方法都有哪些,逐步分析每个方法的使用和源码。

默认的无参构造函数,默认初始容量为10。也就是说创建了一个空的vector它的容量为10,内存分配可装载数据大小为10个元素。

代码语言:javascript
复制
public Vector() {
        this(10);
    }

可以设置初始容量大小的构造函数,初始容量大小和默认的容量不够使用时的扩容大小。

代码语言:javascript
复制
public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

看下它继续调用的关系,然后这里面体现了一种快速失败校验的机制。有的时候可以在程序中进行编写类似的代码来确保程序的健壮性。

代码语言:javascript
复制
public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];//开辟数据空间
        this.capacityIncrement = capacityIncrement;
    }

先暂时看下vector的add方法的使用,为什么vector容器是线程安全的?因为方法上都加了synchronized关键字进行锁定,保证了数据的可见性,一致性,顺序性的特点。

代码语言:javascript
复制
public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);//进行数组的动态扩容机制
        elementData[elementCount++] = e;//在数组的末尾进行添加元素,时间复杂度为O(1)
        return true;
    }
代码语言:javascript
复制
 步骤一: 判断数据是否大于数组的长度
  private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
 步骤二:进行数据的扩容机制
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);//数据之间的拷贝以及引用的指向
    }
步骤三:  进行最大容量的确定,这里也会出现OOM现象
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

为什么ensureCapacityHelper()这个方法没有加锁,主要是为了体现锁粗化的实现,什么是锁粗化?外围方法已经加了锁,里面没有必要再加锁了,后面介绍内容时会对其进行介绍的,这里先暂时说到这吧。

统计数据集合的元素个数:size()方法。

代码语言:javascript
复制
 public synchronized int size() {
        return elementCount;//表示当前集合元素有多少
  }

获取指定元素位置的元素:get()方法。

代码语言:javascript
复制
public synchronized E get(int index) {
//快速失败机制的完美体现哈
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);//根据索引index下标获取元素,因为数组支持
    }

判断数据集合是否为空:isEmpty()方法。

代码语言:javascript
复制
 public synchronized boolean isEmpty() {
        return elementCount == 0;//二元运算符,判断集合元素个数是否等于0
    }

获取数据集合指定元素的索引位置:indexOf()方法。

代码语言:javascript
复制
public int indexOf(Object o) {
        return indexOf(o, 0);
  }
步骤一:循环遍历集合判断待查询数据是否和指定位置的元素相等,若相等返回其下标位置
public synchronized int indexOf(Object o, int index) {
        if (o == null) {
            for (int i = index ; i < elementCount ; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = index ; i < elementCount ; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

上面主要体现了java设计者优秀的能力,由面向过程编程编程了面向对象 编程的体现。

获取数据集合的第一个元素:firstElement()方法。

代码语言:javascript
复制
public synchronized E firstElement() {//加锁
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(0);
    }

这里如果没有集合元素,但是去查找了就会抛出一个运行时异常。

代码语言:javascript
复制
public class NoSuchElementException extends RuntimeException {}

获取数据集合的最后一个元素:lastElement()方法。

代码语言:javascript
复制
public synchronized E lastElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(elementCount - 1);//由于数组下标是从0开始的,所以最后一个元素的下标为elementCount-1
    }

数据集合指定位置的元素替换set()方法。

代码语言:javascript
复制
public synchronized E set(int index, E element) {
//快速失败机制
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;//返回旧值元素
    }

移除指定索引位置的元素:removeElementAt()方法。

代码语言:javascript
复制
public synchronized void removeElementAt(int index) {
        modCount++;
        if (index >= elementCount) {//判断索引是否越界
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {//这里我感觉可以和上面的判断放到一起呀
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        //将后面的元素进行前移
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        elementCount--;//数据元素个数减一,gc进行"垃圾"回收,具体回收时间确定
        elementData[elementCount] = null; /* to let gc do its work */
    }

在指定位置设置指定元素:setElementAt()方法。

代码语言:javascript
复制
public synchronized void setElementAt(E obj, int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        elementData[index] = obj;//将指定元素在对应的索引位置进行替换
    }

移除数据集合中的元素:removeElement()方法。

代码语言:javascript
复制
 public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);//确定要移除元素的索引位置
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
    }

移除数据集合的所有元素:removeAllEmenets()方法。

代码语言:javascript
复制
public synchronized void removeAllElements() {
        modCount++;//这里主要是为了防止并发修改产生的问题
        // Let gc do its work
        for (int i = 0; i < elementCount; i++)
            elementData[i] = null;//gc进行垃圾收集,收集时间不确定

        elementCount = 0;//将集合元素个数置为0
    }

移除数据集合的所有元素:clear()方法。

代码语言:javascript
复制
public void clear() {
        removeAllElements();
    }

上面的基本方法,其用法和写法都很优秀,值得学习,后面进行总结的时候会慢慢输出自己对其的理解,如有不当之处还望指正。接下来我们继续看下其它比较好的设计。

或许曾经我们用过迭代器这种方法来进行数据的获取和遍历集合元素,接下来我们看下Vector是如何获取迭代器进行数据遍历。

代码语言:javascript
复制
public synchronized Iterator<E> iterator() {
        return new Itr();
 }
步骤一:
private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
        //检查是否还存在下一个元素
        public boolean hasNext() {
            return cursor != elementCount;
        }

        public E next() {
            synchronized (Vector.this) {//锁住当前字节码对象.class
                checkForComodification();
                int i = cursor;
                if (i >= elementCount)
                    throw new NoSuchElementException();
                cursor = i + 1;
                return elementData(lastRet = i);
            }
        }

        public void remove() {
            if (lastRet == -1)
                throw new IllegalStateException();
            synchronized (Vector.this) {
                checkForComodification();
                Vector.this.remove(lastRet);
                expectedModCount = modCount;
            }
            cursor = lastRet;
            lastRet = -1;
        }

        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            synchronized (Vector.this) {
                final int size = elementCount;
                int i = cursor;
                if (i >= size) {
                    return;
                }
        @SuppressWarnings("unchecked")
                final E[] elementData = (E[]) Vector.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                }
                while (i != size && modCount == expectedModCount) {
                    action.accept(elementData[i++]);
                }
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
        }
        //检查是否存在并发修改的问题
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

由于现在的集合都支持了Stream流式处理,所以我们这里看下Vector是如何进行元素遍历的。

代码语言:javascript
复制
 public synchronized void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);//判断consumer是否为空
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int elementCount = this.elementCount;
        for (int i=0; modCount == expectedModCount && i < elementCount; i++) {
            action.accept(elementData[i]);//accept接收元素,然后输出
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

这里我们继续看下vector是如何对元素进行排序的。

代码语言:javascript
复制
 public synchronized void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, elementCount, c);
        if (modCount != expectedModCount) {//防止并发修改
            throw new ConcurrentModificationException();
        }
        modCount++;
    }
 步骤一:
 public static <T> void sort(T[] a, int fromIndex, int toIndex,
                                Comparator<? super T> c) {
        if (c == null) {//判断是否存在排序规则,不存在执行sort
            sort(a, fromIndex, toIndex);
        } else {//存在,则基于不同的排序算法进行处理,关于排序的代码这里暂不做分析
            rangeCheck(a.length, fromIndex, toIndex);
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, fromIndex, toIndex, c);
            else
                TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
        }
    }

判断集合是否包含某个元素:contains()方法。

代码语言:javascript
复制
 public boolean contains(Object o) {
        return indexOf(o, 0) >= 0;//判断是否存在索引下标,因为索引下标的查找也是根据元素之间数据的对比进行获取的
    }
 步骤一:
  public synchronized int indexOf(Object o, int index) {
        if (o == null) {
            for (int i = index ; i < elementCount ; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = index ; i < elementCount ; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

到这里Vector这个集合容器,我这里就梳理完了,下面提供了一些如何追踪源码的示例程序,供参考。

代码语言:javascript
复制
package com.wpw.springbootjuc.java8.map;

import lombok.extern.slf4j.Slf4j;

import java.util.Vector;

/**
 * vector源码走读
 *
 * @author wpw
 */
@Slf4j
public class VectorTest {
    public static void main(String[] args) {
        Vector vector = new Vector(10);
        vector.add(1);
        vector.add(2);
        vector.add(3);
        log.debug("[{}]数据集合的遍历", 1);
        vector.forEach(System.out::println);
        log.debug("[{}]返回数据集合元素的大小", 2);
        int size = vector.size();
        log.info("数据大小:[{}]", size);
        log.info("获取集合元素指定位置[{}]的元素:[{}]", 0, vector.get(0));
        log.info("判断数据集合是否为空:[{}]", vector.isEmpty());
        log.info("获取数据集合指定元素的索引位置:[{}]", vector.indexOf(3));
        log.info("获取数据集合的第一个元素:{{}}", vector.firstElement());
        log.info("获取数据集合的最后一个元素:[{}]", vector.lastElement());
        vector.set(2, 4);
        vector.stream().forEach(System.out::println);
        vector.removeElementAt(2);
        vector.stream().forEach(x -> System.out.print(x + "\t"));
        System.out.println();
        vector.setElementAt(4, 1);
        vector.stream().forEach(x -> System.out.print(x + "\t"));
        System.out.println();
        vector.removeElement(4);
        vector.stream().forEach(x -> System.out.print(x + "\t"));
        System.out.println();
        vector.clear();
        vector.stream().forEach(System.out::println);
        boolean flag = vector.contains(1);
        System.out.println("flag = " + flag);
    }
}

分析了Vector学习到了很多东西,在以后的源码分析过程中,自己也会慢慢总结分享出来,如有不当之处还望指正。

最后附上一张部分Vector容器集合的方法截图和Vector继承关系描述。

代码语言:javascript
复制
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档