首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >数据结构之ArrayList

数据结构之ArrayList

作者头像
Qwe7
发布于 2022-04-01 02:59:52
发布于 2022-04-01 02:59:52
29900
代码可运行
举报
文章被收录于专栏:网络收集网络收集
运行总次数:0
代码可运行

首先:讲述ArrayList之前先来说下List,List是java重要的数据结构之一,我们经常接触到的有ArrayList、Vector和LinkedList三种,他们都继承来自java.util.Collection接口

List 是一个接口,它是继承于Collection的接口。它代表着<有序>的队列

下面是Java中的集合类的关系图。从中可以大致了解集合类之间的关系

本篇主要讲述 Arraylist

Arraylist初始化方法,最简短的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> strings = new ArrayList<String>(asList("foo", "bar", "baz"))

Java

COPY

比较常用的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> demo= new ArrayList<>();
demo.add("瞎子");
demo.add("盲僧");
demo.add("李青");
demo.add("扫地僧");

Java

COPY

ArrayList是Java一个很常用的集合类,它相当于一个动态数组,内部的数组大小可以根据元素实际情况自动分配,也可以自己分配大小。

在使用ArrayList的时候,应注意ArrayList并不是线程安全的,如果需要多线程并发操作应当使用CopyOnWriteArrayList(读远大于写的情况),或者使用Collections工具类的synchronizedList方法将其包装。

ArrayList 中常用的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
add(E e):                     	  	在数组末尾添加元素

size():                           	数组中实际元素个数,并不是数组容量
 
add(int index, E e):    	  			在数组指定位置添加元素

clear():                          	将数组中元素清空

contains(E e):             	  		判断数组中是否含有某个元素

get(int index):             	  	返回数组指定位置的元素

indexOf(E e):              	  		返回数组指定元素第一次出现的位置

set(int index, E e):     	  		替换数组指定位置的值

remove(int index):     	 	  		移除数组指定位置的元素

remove(E e):              	  		移除数组中第一次出现的指定元素

addAll(Collection<? extends E> c):      	在数组末尾添加另一个数组 

addAll(int index, collection<? extends E> c):  在数组指定位置添加另一个数组 

removeAll(Collection<?>c):         		将数组中属于数组 c 中的元素全部删除

Java

COPY

上述方法举例说明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.ArrayList;
import java.util.Arrays;
 
 
public class ArrayListTest {
	class Human{
	}
	
	class Male extends Human{	
	}
	
	public static void main(String[] args) {
		ArrayList<Integer> list1=new ArrayList<Integer>();
		list1.add(1); // Appends the specified element to the end of this list
		list1.add(2);
		list1.add(3);
		System.out.println(list1.size());
		System.out.println(list1);
		
		list1.add(2,4); // Inserts the specified element at the specified position in this list
		System.out.println(list1);
		
		list1.clear(); // Removes all of the elements from this list
		System.out.println(list1);
		
		list1.add(5);
		ArrayList<Integer> list2=new ArrayList<Integer>();
		list2.add(1);
		list2.add(2);
		list2.add(3);
		list2.addAll(list1); // Appends all of the elements in the specified collection to the end of this list
		System.out.println(list2);
		
		System.out.println(list2.contains(5)); // Judge if this list contains the specified element
		System.out.println(list2.get(2)); // Returns the element at the specified position in this list
		System.out.println(list2.indexOf(5)); // Returns the index of the first occurrence of the specified element, or -1 if this list doesn't contain
		
		list2.set(2, 10); // Replaces the element at the specified position in this list with the specified element
		System.out.println(list2);
		
		list2.remove(2); // Removes the element at the specified position in this list
		System.out.println(list2); 
		list2.remove(Integer.valueOf(1)); // Removes the first occurrence of the specified element from this list, if it is present
		System.out.println(list2);
		list2.removeAll(Arrays.asList(5)); // Removes from this list all of its elements that are contained in the specified collection
	}
}

Java

COPY

一、基本实现

❤️❤️❤️1、ArrayList和Vector使用了数组实现,可以认为它们封装了对内部数组的操作;它们两个底层的实现基本可以认为是一致的,主要的一点区别在于对多线程的支持上面。ArrayList没有对内部的方法做线程的同步,它不是线程安全的,而Vector内部做了线程同步,是线程安全的。

❤️❤️❤️2、LinkedList使用了双向链表数据结构,与基于数组实现的ArrayList和Vector相比,这是一种不同的实现方式,这也决定了他们不同的应用场景。LinkedList链表由一系列列表项构成,一个表项包含三个部分:元素内容、前驱表项和后驱表项,如下图所示

在JDK的实现中,增加了两个节点指针first、last分别指向首尾节点

二、不同之处

我们拿经常使用ArrayList与LinkedList,以增加和删除元素,比较下不同之处

1、增加元素到列表尾端:

❤️在ArrayList中增加元素到列表尾端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 确保内部数组有足够的空间
        elementData[size++] = e; //将元素加入到数组的末尾,完成添加
        return true;
    }

Java

COPY

在这个过程当时,add的性能主要是由ensureCapacityInternal方法的实现,我们继续往下跟踪源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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++;

        // 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 + (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);
    }

Java

COPY

calculateCapacity方法会根据你对ArrayList初始化的不同,对elmentData这个对象数组进行非空判断。如果它是一个空数组,则返回ArrayList默认容量和新容量比较的最大值,如果不为空则直接返回新容量。接下来在ensureExplicitCapacity方法中判断如果新容量大于elmentData对象数组的长度则调用grow方法对数组进行扩容。

在这里我们可以看到如果ArrayList容量满足需求时,add()其实就是直接对数组进行赋值,性能很高。而当ArraList容量无法满足要求扩容时,需要对之前的数组进行复制操作。因此合理的数组大小有助于减少数组的扩容次数,如果使用时能够预估ArrayList数组大小,并进行初始化,指定容量大小对性能会有所提升。

❤️在LinkedList中增加元素到列表尾端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 //尾端插入,即将节点值为e的节点设置为链表的尾节点
    void linkLast(E e) {
        final Node<E> l = last;
        //构建一个前驱prev值为l,节点值为e,后驱next值为null的新节点newNode
        final Node<E> newNode = new Node<>(l, e, null);
        //将newNode作为尾节点
        last = newNode;
        //如果原尾节点为null,即原链表为null,则链表首节点也设置为newNode
        if (l == null)
            first = newNode; 
        else  //否则,原尾节点的next设置为newNode
            l.next = newNode;
        size++;
        modCount++;
    }

Java

COPY

LinkedList由于使用了链表结构,因此不需要维护容量的大小,这是相比ArrayList的优势。但每次元素的增加都需要新建一个node对象,并进行更多的赋值操作。在大数据量频繁的调用过程中,对性能会有所影响。

2、增加元素到任意位置:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void add(int index, E element)

Java

COPY

由于实现上的不同,ArrayList和LinkedList在这个方法上存在存在一定的性能差异。由于ArrayList是基于数组实现的,而数组是一块连续的内存空间,如果在数组的任意位置插入元素,必然导致在该位置后的所有元素需要重新排列,因此效率会比较低。

❤️ArrayList代码实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //数组复制
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

Java

COPY

可以看到,ArrayList每次插入操作,都会进行一次数组复制。并且插入的元素在List中位置越靠前,数组重组的开销也越大。

❤️再看LinkedList代码实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    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;
        }
    }
void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    //指定节点的前驱prev
    final Node<E> pred = succ.prev;
    //当前节点的前驱为指点节点的前驱,后继为指定的节点
    final Node<E> newNode = new Node<>(pred, e, succ);
    //更新指定节点的前驱为当前节点
    succ.prev = newNode;
    //更新前驱节点的后继
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

Java

COPY

LinkedList中定位一个节点需要遍历链表,如果新增的位置处于List的前半段,则从前往后找;若其位置处于后半段,则从后往前找。因此指定操作元素的位置越靠前或这靠后,效率都是非常高效的。但如果位置越靠中间,需要遍历半个List,效率较低。因此LinkedList中定位一个节点需要遍历链表,所以下标有关的插入、删除时间复杂度都变为O(n) ;

3、删除任意位置元素
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public E remove(int index) 

Java

COPY

对ArrayList来说,remove()方法和add()方法是相同的,在删除指定位置元素后,都要对数组进行重组。代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            //移动数组
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

Java

COPY

可见,在进行一次有效删除后,都要进行数组的重组。并且跟add()指定位置的元素一样,删除元素的位置越靠前,重组时的开销就越大,删除的元素位置越靠后,开销越小

❤️再看LinkedList中代码的实现如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

   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;
        }
    }

  E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next; //当前节点的后继
    final Node<E> prev = x.prev; //当前节点的前驱

    if (prev == null) {
        first = next;
    } else {
        prev.next = next; //更新前驱节点的后继为当前节点的后继
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev; //更新后继节点的前驱为当前节点的前驱
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
} 

Java

COPY

可见跟之前的插入任意位置一样,LinkedList中定位一个节点需要遍历链表,效率跟删除的元素的具体位置有关,所以删除任意位置元素时间复杂度也为O(n) ;

4、随机访问
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public E get(int index)

Java

COPY

首先看ArrayList的实现代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

Java

COPY

可见ArrayList随机访问是直接读取数组第几个下标,效率很高。 LinkedList实现代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    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;
        }
    }

Java

COPY

相反LinkedList随机访问,每次都需要遍历半个List确定元素位置,效率较低。

优雅的去除list外层两端的 [ ],直接显示列表内容

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[1,2,3,4,5,6]
StringUtils.strip(list.toString(), "[]")
1,2,3,4,5,6

本文系转载,前往查看

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

本文系转载,前往查看

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

评论
登录后参与评论
暂无评论
推荐阅读
第二代半导体GaAs你了解多少?
最近国家十四五规划,把第三代半导体作为重点发展对象,大家都知道第三代半导体以SiC、GaN为代表。但是今天我们聊聊第二代半导体之一的GaAs(另外一个是磷化铟)。
用户2760455
2022/06/08
1.4K0
第二代半导体GaAs你了解多少?
激光雷达Lidar里面的激光
雷达作为车辆避障的重要手段,现在已经从最初仅有超声波雷达发展到超声波雷达、毫米波雷达和激光雷达互补共存的阶段,激光雷达以其分辨率高的优势,迎来快速增长的时期,无人驾驶技术已是大势所趋,车载的激光雷达近几年出现爆发式增长的局面。
用户2760455
2022/06/08
7730
激光雷达Lidar里面的激光
DFB激光器
DFB,Distributed Feedback Laser,即分布式反馈激光器。DFB激光器主要以半导体材料为介质,包括锑化镓(GaSb)、砷化镓(GaAs)、磷化铟(InP)、硫化锌(ZnS)等。DFB激光器具有非常好的单色性(即光谱纯度),它的线宽普遍可以做到1MHz以内,以及具有非常高的边模抑制比(SMSR),可高达40-50dB以上。
用户2760455
2022/06/08
1.5K0
DFB激光器
激光器的漏电流
漏电流,听名字就知道是个比较负能量的一个东西。在半导体领域,二极管在反向截止的时候,并不是完全理想的截止。在承受反压的时候,会有些微小的电流从阴极漏到阳极。这个电流通常很小,而且反压越高,漏电流越大,温度越高,漏电流越大。大的漏电流会带来较大的损耗,特别在高压应用场合。 产生的原因:从半导体材料内部结构看,是外加反向电压在PN结势垒区所产生的反向电场E大于势垒区扩散电荷形成的电场E。,导致了通过PN结的反向漏电电流。势垒区的薄厚,以及所加反向电压的大小共同决定了漏电电流的大小。
用户2760455
2022/06/08
4610
激光器的漏电流
复习一下激光器和激光器芯片
半导体激光器俗称激光二极管,因为其用半导体材料作为工作物质的特性所以被称为半导体激光器。半导体激光器由光纤耦合半导体激光器模块、合束器件、激光传能光缆、电源系统、控制系统及机械结构等构成,在电源系统和控制系统的驱动和监控下实现激光输出。半导体激光器的常用工作物质主要有砷化镓(GaAs)、硫化镉(CdS)、磷化铟(InP)、硫化锌(ZnS)等。根据不同的工作物质主要有三种激励方式:电注入,pump式和高能电子束激励。
用户2760455
2022/06/08
1.1K0
复习一下激光器和激光器芯片
半导体激光器芯片减薄、拋光工艺
激光器都是在外延的基础上做出芯片的结构设计,外延厚度2寸的在350um,4寸的在460um左右,到芯片后期,都需要对晶圆进行减薄,厚度有做到80um的,也有120um的,通常为兼顾芯片性能和破片良率问题,100um的厚度成为常见厚度。
用户2760455
2022/11/15
1.3K0
半导体激光器芯片减薄、拋光工艺
Vcsel芯片和制作流程
但凡说到激光器,人们必须提及Vcsel,也就是垂直腔面发射激光器:Vertical-Cavity Surface-Emitting Laser。
用户2760455
2022/11/15
1.3K0
Vcsel芯片和制作流程
大功率半导体激光器
1962 年,美国科学家宣布成功研制出了第一代半导体激光器———GaAs 同质结构注入型半导体激光器。由于该结构的激光器受激发射的阈值电流密度非常高,需要 5 × 10^4 ~ 1 × 10^5 A/ cm2,因此它只能在液氮制冷下才能以低频脉冲状态工作。从此开始,半导体激光器的研制与开发利用成为人们关注的焦点。
用户2760455
2022/06/08
1.4K0
大功率半导体激光器
可见光激光器---二
半导体可见光激光器的色系发展同LED同步。在1968年红色LED在美国问世。红、绿、蓝三基色一直是人们追求的单色光。在20世纪70年代人们实现了GaAlAs/GaAs 0.8um--0.85um的短波长异质结结构的半导体激光器。并在InP上实现了四元InGaAsP发射1.3um-1.55um的室温连续光谱,在光通信领域取得巨大应用。
用户2760455
2022/06/08
5170
可见光激光器---二
Vcsel芯片和边发射激光芯片
边发光激光芯片依靠衬底晶体的解离面作为谐振腔面,在大功率以及高性能要求的芯片上技术已经成熟,但是也存在很多不足,例如激光性能对腔面的要求较高,不能用常规的晶圆切割,比如砂轮刀片、激光切割等。在实际生产中测试环节又特别麻烦,需要解离成bar条才能继续测试,这很消耗切片的能力,而且非费劲切出来,结果测试还不一定过。
用户2760455
2022/06/08
2K0
Vcsel芯片和边发射激光芯片
MMIC技术:伪形态高电子迁移率晶体管(pHEMT)
伪形态高电子迁移率晶体管(pHEMT)是单片微波集成电路(MMIC)设计人员和晶圆厂用来开发和制造微波集成电路的一种技术。pHEMT因其卓越的宽带性能特性,包括低噪声系数,高OIP3和高达40 GHz及以上的出色可靠性,作为电子制造商(如Mini-Circuits)生产的许多MMIC的构建模块而广受欢迎。pHEMT使用不同成分的半导体和带隙之间的异质结来实现出色的高频性能。本文深入探讨了pHEMT操作的物理原理、优势和可靠性测试结果。还提供了Mini-Circuits的pHEMT产品摘要的链接。
海大指南针
2022/05/16
5.5K0
MMIC技术:伪形态高电子迁移率晶体管(pHEMT)
DFB分布反馈激光器:设计和制作
法布里-珀罗激光器(FP-LD)是最常见、最普通的半导体激光器,它最大的特点是激光器的谐振腔由半导体材料的两个解理面构成。目前光纤通信上采用的FP-LD的制作技术已经相当成熟,普遍采用双异质结多量子阱有源层、载流子与光分别限制的结构。
用户2760455
2022/06/08
3.4K0
DFB分布反馈激光器:设计和制作
几篇不错的激光芯片专利
1 2020年美国专利商标局发布了苹果公司的一项名为“使用量子阱混合技术的激光架构”的专利申请。该专利的发明人来自苹果的工程团队,负责为苹果产品设计未来的专用芯片。目前尚不清楚是否扩展到新的Apple Silicon。
用户2760455
2022/06/08
7790
几篇不错的激光芯片专利
LED之父去世,8年前颁给LED的诺奖却没有他
羿阁 萧箫 发自 凹非寺 量子位 | 公众号 QbitAI “颁给LED的诺奖却没颁给他,是一种不公。” 就在这几天,LED之父去世了,享年93岁。 Nick Holonyak,第一位可见光LED的发明者,掀起了人类自爱迪生发明电灯泡以来照明史的第二次革命。 ——甚至可以说,没有他这份发明,就没有今天“遍地开花”的LED照明灯。 凭借这一发明,他斩获了无数奖项,老布什、小布什、日本天皇、普京都为他颁过奖,他的名字甚至被美国光学学会设立成奖项,颁给每年在半导体领域做出杰出贡献的人。 但遗憾的是,这里面唯独少
量子位
2022/09/27
3380
LED之父去世,8年前颁给LED的诺奖却没有他
5G对半导体芯片材料的需求
芯片的好坏,一靠设计、二靠制作工艺。最后还是靠材料。半导体材料可分为单质半导体及化合物半导体两类,前者如硅(Si)、锗(Ge)等所形成 的半导体,后者为砷化镓(GaAs)、氮化镓(GaN)、碳化硅(SiC)等化合物形成。半导 体在过去主要经历了三代变化,。砷化镓(GaAs)、氮化镓(GaN)和碳化硅(SiC)半 导体分别作为第二代和第三代半导体的代表,相比第一代半导体高频性能、高温性能优 异很多,制造成本更为高昂,可谓是半导体中的新贵。
用户2760455
2022/06/08
7020
5G对半导体芯片材料的需求
硅基III-V族量子点激光器
这一篇笔记调研下硅基III-V族量子点激光器。之前的笔记 硅光芯片的光源中提到在Si上直接外延生长III-V族材料,进而加工结构形成激光器。本篇笔记对此做更详细的介绍。
光学小豆芽
2020/08/13
1.7K0
惠普实验室的DWDM硅光平台
惠普实验室(Hewlett Packard Labs, 以下简称HPL)的硅光平台,主要特色是异质集成了InAs/GaAs材料,既实现了片上的量子点频率梳激光器,也实现了MOSCAP型调制器,其主要工艺步骤如下图所示,
光学小豆芽
2022/12/02
1.4K0
惠普实验室的DWDM硅光平台
半导体材料知识点
原子由三种不同的粒子构成:中性中子和带正电的质子组成原子核,以及围绕原子核旋转的带负电核的电子,质子数与电子数相等呈现中性。
用户2760455
2022/06/08
1.3K0
半导体材料知识点
惠普实验室:大规模III-V/Si异质集成光子器件平台助力下一代光计算(一)
(原文链接:https://ieeexplore.ieee.org/document/10835188)
光芯
2025/04/08
2460
惠普实验室:大规模III-V/Si异质集成光子器件平台助力下一代光计算(一)
光纤通信是怎么实现的?
前言:飞鸽传书很有意思。据说楚汉战争时期,刘邦被项羽包围,这时候刘邦就是采用飞鸽传书的方式向总部求援,最终成功脱险。
通往ICT之路
2024/04/09
2270
光纤通信是怎么实现的?
相关推荐
第二代半导体GaAs你了解多少?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档