JDK8: Collection

news/2024/7/5 2:17:51

序言

整理下JDK的集合的设计思路,以便我们在开发过程中创建自己的数据结构cuiyaonan2000@163.com

ArrayList

以ArrayList增加一个元素为入口.

 /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

如上新增一个元素重点是ensureCapacityInternal(size+1) 这句话

ensureCapacityInternal的方法声明如下所示

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

其中calculateCapacity的声明如下,其主要作用是返回size + 1

这里有个隐藏的优化,在使用new ArrayList()的时候elementData[] 默认是空的,只有在第一次add()元素的时候才会创建一个长度为10的elementData[10]的数组对象cuiyaonan2000@163.com

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        
        /**
         * 如果在声明空的ArrayList的时候使用的就是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,只有在新增第一个元素的时候才会实例化 elementData[]
        */
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

那为什么针对空集合也需要设置增加长度1呢?JDK的目的如下cuiyaonan2000@163.com

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

在后去到当前新增一个元素后的 集合长度后 在调用如下的方法,主要是用来判断是否已经超过集合的长度,如果超过了就进行扩容,当然也不是简单的扩容还是有一定的计算的cuiyaonan2000@163.com

private void ensureExplicitCapacity(int minCapacity) {

        //用于统计扩容的次数 ,这个还是有用的,在我们常在内存的对象,我们可以统计下,该集合扩容了多少次cuiyaonan2000@163.com
        modCount++;

        //判断是否需要扩容
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

扩容操作:

 /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;

        //这里是每次扩容的量:即每次扩容一半,比如原来是10 这现在是10+5即15
        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);
    }

这里有个 问题即当数组超过了Integer最大长度后会调用hugeCapacityz()这个方法返回一个最终长度cuiyaonan2000@163.com

 if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
方法参考如下
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

LinkedList

这个相比ArrayList就数据接口相对简单点,他里面没像ArrayList的数组elementData[] 来存储数据.

同理看下它的add()方法的实现

 /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

linklast的实现如下所示:

/**
     * 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++;

        //扩容次数与size一样大.
        modCount++;
    }

Vector

线程安全的集合的实现就是 在入口增加了synchronized.以保证安全,此外无参的构造函数跟ArrayList是不一样的.直接创建一个elementData[10],而ArrayList默认创建的是一个空的elementData[],arrayList只在新增一个元素的时候才会去真正的实例化一个elementData[10]切记cuiyaonan2000@163.com

 /**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

如上ensureCapacityHelper也是用来判断是否需要来进行扩容的.

    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

具体扩容由grow() 方法来设置

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;


        //主要是这里默认是 增加一倍的储量 比如原来是10,则增加10
        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);
    }

HashMap

存储数据的对象不是elementData[],而是

transient Node<K,V>[] table;

Node的定义如下所示:(Node对象有4个属性,注意 哪些是final修饰的)

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

由此先建立一个Node[] 是数组集合,每个Node还有个next属性来指向下一个Node形成了链表cuiyaonan2000@163.com

同样以put(k,v)来学习

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

hash就不用说了,直接看putVal()方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }


http://lihuaxi.xjx100.cn/news/104569.html

相关文章

jQuery的下载与安装

引言 在学习后端Java的同时也少不了前端的知识&#xff0c;而jQuery则是现在前端人员普遍都在使用的一个框架&#xff0c;其可以更好的帮助我们使用前端语言&#xff0c;那么今天我们就先来简单地讨论一下如何对其进行安装和下载吧。 下载与安装 ​我们要想使用 jQuery&#x…

JSP的简化:一文吃透EL表达式

本文被 系统学习JavaWeb 收录点击订阅专栏 文章目录1 走进EL表达式2 关于EL表达式与Bean对象2.1 什么是Java Bean&#xff1f;2.2 使用EL表达式输出复杂Bean对象3 EL表达式的运算3.1 关系运算3.2 逻辑运算3.3 算术运算3.4 empty运算3.5 三元运算3.6 点运算和中括号运算4 EL表达…

手动搭建K8S环境

手动搭建K8S环境 主机名ip系统版本k8s-master172.16.200.70Centos7k8s-node1172.16.200.71Centos7k8s-node2172.16.200.72Centos7 前期准备好三台Centos7机器&#xff0c;均配置如下&#xff1a; # 关闭防火墙 systemctl stop firewalld systemctl disable firewalld # 永久关…

二叉树深度和高度你分得清么?

104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 看题解前&#xff1a;这里需要知道什么是深度&#xff0c;什么是高度。深度指的是从根结点出发到叶子结点中间的节点个数&#xff1b;高度指的是从该节点出发到叶子节点中间的个数。所以说根节点的高度也就是该二…

语音处理-傅里叶分析和Z变换

语音处理-傅里叶分析和Z变换 时域与频域 •信号可以表示任何类型的序列测量 •信号通常表示序列时间上的测量 •信号分析的一项有用技术是将其分解为一组基本部分易于理解&#xff08;例如线性趋势&#xff09; •分解为周期函数可以是将信号转换为“频域”&#xff08;即时间…

JQuery详解(讲解+举例)--(后端开发适用)

JQuery JQueryJQuery1.JQuery内容2. JQuery对象2.1.Jquery的下载与安装2.1.1.下载2.1.3.优点2.1.4.安装2.2.Jquery核心2.3.Dom对象与Jquery包装集对象2.3.1 javascript中获取Dom对象&#xff0c;Dom对象只有有限的属性和方法&#xff1a;2.3.2.Jqueryt包装集对象2.3.3.Dom对象转…

Web3.0 对网络安全世界的影响

随着全球技术格局在过去十年中不断发展&#xff0c;引起许多人兴趣的一个概念是“Web3”。从最基本的意义上说&#xff0c;Web3 可以被视为互联网的迭代&#xff0c;它是去中心化的&#xff0c;并授予用户以安全、点对点方式相互交互的能力&#xff08;即无需任何中心化中介&am…

idea打包maven项目及python3调用jar包

1、目标 解决java组件依赖的问题&#xff1a;将依赖java实现的程序封装后&#xff0c;打成可执行的jar包&#xff0c;再通过python3执行调用即可。 2、核心步骤 1&#xff09;选择合适的框架&#xff0c;如maven&#xff0c;并引入依赖包(pom.xml) 2&#xff09;封装主程序后&…