jdk 集合大家族之Collection

前言:

  此处的集合指的是java集合框架中的实现了Collection接口相关的类。所以主要为List Set 和 Queue 其他章节会专门介绍Map相关。

1. List

1.1 ArrayList

  • 从数组中间删除某个元素需要很大代价,因为被删除之后的元素都要向数组的前端移动
  • 适合查找和修改
  • ArrayList底层通过数组实现
    • 顺序存储
    • 读取 存入操作方便
    • 插入、删除操作不方便

1.1.1 ArrayList分析 (jdk 8)

从ArrayList 的初始化开始说起
  • 无参构造函数

    • 创建默认容量为10的数组
    /**
    * Constructs an empty list with an initial capacity of ten.
    */
    public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • 有参构造函数
    • 根据容量的
    /**
    * Constructs an empty list with the specified initial capacity.
    *
    * @param initialCapacity the initial capacity of the list
    * @throws IllegalArgumentException if the specified initial capacity
    * is negative
    */
    public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    // 直接指定 没有扩容操作
    this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
    this.elementData = EMPTY_ELEMENTDATA;
    } else {
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    }
    }
    • 使用集合作为参数
    /**
    * Constructs a list containing the elements of the specified
    * collection, in the order they are returned by the collection's
    * iterator.
    *
    * @param c the collection whose elements are to be placed into this list
    * @throws NullPointerException if the specified collection is null
    */
    public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
    elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
    // replace with empty array.
    this.elementData = EMPTY_ELEMENTDATA;
    }
    }
类的成员变量
    private static final long serialVersionUID = 8683452581122892189L;

    /**
* Default initial capacity.
*/
// 默认容量
private static final int DEFAULT_CAPACITY = 10; /**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {}; /**
* 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 = {}; /**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access /**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size; // 数组最大的长度 其中通过最终的hugeCapacity()可以突破这个限制达到Integer.MAX_VALUE
// 数组需要-8: 有些虚拟机在数组中保留了一些头信息。避免内存溢出
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

需要注意的是ArrayList存储的最大量其实跟你设置的内存也是有关系的。

ArrayList 的删除操作
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
} /*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
// 将index后面的舒服复制到index处
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//置空
elementData[--size] = null; // clear to let GC do its work
}
ArrayList 的扩容
  • 通过grow函数 创建一个新的数组,赋予新的长度然后覆盖掉原来的数组
   // 添加函数
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//空数组扩容默认值和需要的长度 取最大值
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
// modCount: 继承自AbstractList 用于防止一个迭代器(Iterator)对集合进行修改而另一个迭代器对其进行遍历时,产生混乱的现象(并发场景下)。与modCount 搭配的是 expectedModCount
modCount++; // overflow-conscious code
// 需要的长度比数组长度大,此时确实需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 目标扩容为原数组长度的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 需要的长度比计算值大的话 取最大值
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果计算后的值比ArrayList 的MAX_ARRAY_SIZE属性还要大的话,进行终极扩容
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);
} private static int hugeCapacity(int minCapacity) {
// 溢出
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 最大值为Integer.MAX_VALUE 此时多余的会被舍弃掉
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

1.2 LinkedList

  • 随机访问第n个元素需要从头开始,如果大于size()/2 则从尾端开始搜索
  • 相比数组实现的ArrayList
    • 提供了无界队列功能下面有说到
    • 插入删除时较arrayList 好
    • 适合插入和删除
  • ListIterator 接口提供两个方法可以反向遍历链表
    • previous
    • hasPrevious

1.3 Vector

  • 线程安全 因为需要同步所以会耗费大量时间
  • 通过synchronized实现加锁

2. Set

2.1 HashSet

散列集

  • 通过散列表为每个对象计算一个散列码,也称之为hash code 它是一个整数(可以是负数)
  • 通过链表数组实现
    • 数组的部分被称之为桶(bucket)
    • 桶默认16个
    • 当装填因子0.75,表中超过75%的位置已经被填入则会用双倍的桶数自动进行再散列(扩容并重新计算hash code)
    • jdk8 中桶满时会从链表变为平衡二叉树
  • 相比较于List
    • 不可重复
    • 无序
    • 可快快速查看某个元素,而不必遍历List来查找
  • 底层实现通过HashMap
    • 将值放到key中 value为空的object

2.2 TreeSet

  • 树集比散列集有所改进,是一个有序的集合

    • 因此迭代器是以排好的顺序来访问每个元素的
    • 排序的规则是实现 Coompareable接口或使用Comparator
  • 排序是通过树结构来完成的(红黑树)
  • 相比较散列集而言,插入时会有一个排序的过程,所以会慢一些(慢多少是根据数据而言,或许相差很小)

3. Queue

  • Deque: 双端队列 ,有两个端头的队列。不支持在队列中间添加元素
  • java 中的链表都是都是双向链接的
  • 队列的实现方式有两种: 数组或链表

3.1 ArrayDeque

  • 通过数组实现
  • 有边界 但是效率高

3.2 LinkedList

  • 通过链表实现
  • 无边界 没有数组实现效率高

3.3 PriorityQueue

优先级队列

  • 优先级队列的典型案例是任务调度

    • 每当启动一个新的任务时,都将优先级最高的任务从队列中删除)
    • 1 设为“最高” 优先
  • 通过堆来实现的 ,自我调整的二叉树 可以在删除和添加操作时,让最小的元素移动到根。

比较

  • 如果需要收集的对象没有上限就使用链表,如果是一个有界的(容量有限),就使用数组实现。
参考资料
  • java核心技术卷一

jdk 集合大家族之Collection的更多相关文章

  1. jdk 集合大家族之Map

    jdk 集合大家族之Map 前言: 之前章节复习了Collection接口相关,此次我们来一起回顾一下Map相关 .本文基于jdk1.8. 1. HashMap 1.1 概述 HashMap相对于Li ...

  2. java提高篇(二十)-----集合大家族

          在编写java程序中,我们最常用的除了八种基本数据类型,String对象外还有一个集合类,在我们的的程序中到处充斥着集合类的身影!java中集合大家族的成员实在是太丰富了,有常用的Arra ...

  3. java集合-集合大家族

    在编写 Java 程序中,我们最常用的除了八种基本数据类型,String 对象外还有一个集合类,在我们的的程序中到处充斥着集合类的身影!Java 中集合大家族的成员实在是太丰富了,有常用的 Array ...

  4. java 集合大家族

    在编写java程序中,我们最常用的除了八种基本数据类型,String对象外还有一个集合类,在我们的的程序中到处充斥着集合类的身影!java中集合大家族的成员实在是太丰富了,有常用的ArrayList. ...

  5. 01 语言基础+高级:1-6 集合_day02【Collection、泛型】

    day02[Collection.泛型] 主要内容 Collection集合 迭代器 增强for 泛型 教学目标 能够说出集合与数组的区别 说出Collection集合的常用功能 能够使用迭代器对集合 ...

  6. Java 集合系列 02 Collection架构

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  7. 关于MongoDB 固定集合(capped collection)的知识梳理

    一 . 什么是固定集合 MongoDB中有一种特殊类型的集合,值得我们特别留意,那就是固定集合(capped collection). 固定集合可以声明collection的容量大小,其行为类似于循环 ...

  8. Java学习--java中的集合框架、Collection接口、list接口

    与数组相比:1.数组的长度固定,而集合的长度可变2.数组只能通过下表访问元素,类型固定,而有的集合可以通过任意类型查找所映射的具体对象 java集合框架:collection(list序列,queue ...

  9. MongoDB固定集合(capped collection)

    一 . 什么是固定集合 MongoDB中有一种特殊类型的集合,值得我们特别留意,那就是固定集合(capped collection). 固定集合可以声明collection的容量大小,其行为类似于循环 ...

随机推荐

  1. PowerShell启用多跳远程控制

    有些场景下,我们使用远程连接了某个Server,在远程Server中再想进行远程操作,就会提示错误.因为默认的认证信息只会传递一跳. 对此,微软官方文档给出的解决方案是:https://docs.mi ...

  2. Kubernetes部署Prometheus+Grafana(非存储持久化方式部署)

    1.在master节点处新建一个文件夹,用于保存下载prometheus+granfana的yaml文件 mkdir /root/prometheus cd /root/prometheus git ...

  3. Leetcode(2)-两数相加(包含链表操作的注意事项)

    给定两个非空链表来表示两个非负整数.位数按照逆序方式存储,它们的每个节点只存储单个数字.将两数相加返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. 示例: 输入:(2 -& ...

  4. HDU 4272 LianLianKan(状压DP)题解

    题意:一个栈,每次可以选择和栈顶一样的数字,并且和栈顶距离小于6,然后同时消去他们,问能不能把所有的数消去 思路:一个数字最远能消去和他相距9的数,因为中间4个可以被他上面的消去.因为还要判断栈顶有没 ...

  5. apache 解析漏洞(CVE-2017-15715)

    在p牛博客最近更新的文章,传送门,感觉很有意思,自己在自己本地测试了一下 0x01 正则表达式中的 '$' apache这次解析漏洞的根本原因就是这个 $,正则表达式中,我们都知道$用来匹配字符串结尾 ...

  6. MacOS微信逆向分析-Frida

    MacOS微信逆向分析-Frida 0.前言 PC下的微信二次开发相信大家都会了,那么本篇文章将带领大家使用Frida框架对Mac下微信来进行二次开发! PS:还有一种静态注入的方式也不错,但是考虑到 ...

  7. Redis in Action : Redis 实战学习笔记

    1 1 1 Redis in Action : Redis  实战学习笔记 1 http://redis.io/ https://github.com/antirez/redis https://ww ...

  8. 如何使用 js 写一个正常人看不懂的无聊代码

    如何使用 js 写一个正常人看不懂的无聊代码 代码质量, 代码可读性, 代码可维护性, clean code WAT js WTF https://www.destroyallsoftware.com ...

  9. 如何重置电信悦 me 智能网关

    如何重置电信悦 me 智能网关 重置电信网关密码 电信悦 me 智能网关密码忘记了怎么办? 首先,得要知道默认终端配置地址和默认终端配置密码. 可以从无线路由器背面标签得知. 如果不知道密码了,可以通 ...

  10. auto embedded component in an online code editor

    auto embedded component in an online code editor how to auto open a component in the third parts onl ...