草庐IT

Java集合框架之List

文件精度 2023-04-17 原文

1.List集合概要

2.Iterable接口

1.概要

2.重要方法
  1. forEach方法:对Collection集合中的每个对象进行消费
List<Student> list = Stream.generate(() -> new Student("张三", 23)).limit(100).collect(Collectors.toList());
list.forEach(System.out::println);
  1. spliterator方法:获取Spliterator迭代器

3.Collection接口

1.重要方法
  1. spliterator():创建Spliterator
  2. stream():创建串行流Stream
  3. parallelStream():创建并行流Stream

4.RandomAccess接口

1.概要

这是一种标识型接口,用于标识某个类具有某种功能特性的接口。

5.Vector

1.Vector集合概要

Vector是一个线程安全的集合,其中的大部分方法都加了synchronized关键字。

2.重要变量
  1. Vector中重要的变量信息
// 集合Vector存放对象的数组
protected Object[] elementData;
// 集合Vector存储的对象数量
protected int elementCount;
// elementData数组每次扩容的大小
protected int capacityIncrement;
3.Vector的扩容操作
  1. 初始化Vector的扩容操作
// 1. 指定了初始化容量和每次扩容时的增量
// 则下次扩容为initialCapacity + capacity,依次类推
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

// 2. 指定初始化容量和每次扩容时的增量为0
// 则下次扩容为2 * initialCapacity
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}

// 3.指定初始化容量为10,则下次扩容为20, 40...
public Vector() {
    this(10);
}
  1. 向集合Vector添加对象,elementData数组容量不足时:2倍扩容
// minCapacity为期望的elementData数组容量
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 客户指定的每次数组扩容大小超过0,则扩容为newCapacity = oldCapacity + capacityIncrement
    // 否则进行2倍扩容
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    // 确保数组扩容为期望的elementData数组容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  1. 客户端重新设置Vector的容量时,可能会进行扩容操作
public synchronized void setSize(int newSize) {
    modCount++;
    // 新容量大于当前Vector集合中的对象数量则进行扩容
    if (newSize > elementCount) {
        ensureCapacityHelper(newSize);
    } else {
        for (int i = newSize ; i < elementCount ; i++) {
            elementData[i] = null;
        }
    }
    elementCount = newSize;
}

6.ArrayList

1.ArrayList概要

ArrayList是一个多线程环境下有些操作不安全的集合

2.ArrayList集合中重要的常量和变量
// 默认的初始化数组容量
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存放集合ArrayList中的对象的数组
transient Object[] elementData;
// 集合ArrayList中包含的对象数量
private int size;
3.ArrayList集合的初始化操作
// 使用指定的初始化容量进行初始化
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);
    }
}

// 构造一个长度为0的ArrayList集合,
// 后面使用add方法添加元素会将elementData扩容成长度为10的数组
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 根据另一个集合构造ArrayList
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;
    }
}
4.ArrayList的扩容操作
  1. 扩容:按照1.5倍扩容
// minCapacity为期望的数组最低容量(即原数组容量 + 1)
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 1.5倍进行扩容
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 确保容器达到最低容量10,最低下限
    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);
}
5.ArrayList内部数组的移动操作
  1. 移动数组元素的操作都是通过System.arraycopy这个本地方法完成。
6.ArrayList的删除操作removeIf
  1. 删除操作removeIf方法中使用BitSet根据相应的位标记需要删除的元素。
// size为容器中的元素个数
final BitSet removeSet = new BitSet(size);
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
    @SuppressWarnings("unchecked")
    final E element = (E) elementData[i];
    if (filter.test(element)) {
        // 需要删除的元素标记为1
        removeSet.set(i);
        // 统计需要删除的元素个数
        removeCount++;
    }
}

7.Vector和ArrayList的对比
  1. 两者的内部结构都是数组。
  2. Vector集合默认采用当前容量的1倍大小进行扩容,且可以指定一个固定的扩容增量;ArrayList再进行扩容操作时会将当前容量增大50%。
  3. Vector集合大大部分操作都是线程安全的,但是使用的是synchronized锁,锁的粒度太过粗放,保证线程安全性不推荐使用Vector;ArrayList集合不是线程安全的,在多线程环境下不能使用它。
  4. Vector集合在序列化的过程中,当前elementData数组中多余的索引位被序列化,产生不必要的性能消耗;在对ArrayList集合进行序列化时,只会对elementData数组中已使用的索引位进行序列化,未使用的索引位不会被序列化;相对地,在原ArrayList集合中已被序列化的各个数据对象被反序列化成新的ArrayList集合中的数据对象时,新的elementData数组不会产生多余的容量,只有在下一次被要求向该集合中添加数据对象时,才会开始新一轮的扩容操作。

7.Stack

1.Stack概要

2.Stack和Vector的对比
  1. Stack继承自Vector,内部结构都是数组,都存在数组扩容的问题,每次将数组容量增大一倍的方式不灵活。
  2. 两者都是线程安全的,但是不推荐使用。已经被LinkedBlockingQueue和CopyOnWriteArrayList等替代。
  3. 两者没有针对序列化过程和反序列化过程进行任何优化,而工作效果相似的ArrayDeque集合、ArrayList集合都对序列化过程的反序列化过程进行了优化。

8.LinkedList

1.LinkedList集合的继承体系

2.LinkedList中重要的变量
  1. LinkedList底层由双向链表实现
// 集合LinkedList的对象个数
transient int size = 0;
// 指向头节点(双向链表的第一个节点)
transient Node<E> first;
// 指向尾节点(双向链表的最后一个节点)
transient Node<E> first;

// 双链表节点结构
private static class Node<E> {
        // 节点存储的元素
        E item;
        // 指向下一个节点
        Node<E> next;
        // 指向前一个节点
        Node<E> prev;
}
3.LinkedList集合的添加操作
  1. linkFirst(E e):在双链表头部插入一个新节点
  2. linkLast(E e):在双链表尾部插入一个新节点
  3. linkBefore(E e, Node succ):在指定节点前插入一个新节点
4.LinkedList集合的移除操作
  1. unlinkFirst(Node f):移除双链表的第一个节点
  2. unlinkLast(Node l):移除双链表的最后一个节点
  3. unlink(Node x):移除双链表的指定节点
5.LinkedList的查找操作
  1. 查询指定索引位置的节点
// 根据索引和集合长度的一半对比,来决定在头部还是尾部开始搜索。
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

6.LinkedList和ArrayList的对比
  1. 基于数组结构的ArrayList的删除和插入操作效率不高,需要考虑数组扩容元素复制、移动数组元素的问题;基于双向链表的LinkedList的插入和修改操作需要考虑查询索引位的节点的时间消耗,如果在靠近双向链表的中间位置插入新的节点,则找到正确的索引位置节点的时间复杂度为o(n)。
  2. ArrayList集合支持随机访问,所以查询的效率很高,时间复杂度为O(1);LinkedList集合的查询操作时间复杂度为o(n);

有关Java集合框架之List的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  3. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  6. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  7. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  8. TimeSformer:抛弃CNN的Transformer视频理解框架 - 2

    Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

随机推荐