Java 数据结构深入理解ArrayList与顺序表

目录
  • 一、ArrayList简介
  • 二、ArrayList的使用
    • 1、ArrayList的构造
    • 2、ArrayList的遍历
    • 3、ArrayList的常见操作(方法)
    • 4、ArrayList的扩容机制
  • 三、模拟实现一个顺序表(Object[])

一、ArrayList简介

在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表。

二、ArrayList的使用

1、ArrayList的构造

方法 使用
ArrayList() 无参构造
ArrayList(Collection<? extends E> c) 利用其他 Collection 构建 ArrayList
ArrayList(int initialCapacity) 指定顺序表初始容量
public static void main(String[] args) {
        // 无参构造
        List<Integer> list1 = new ArrayList<>();
        // 给定初始容量
        List<Integer> list2 = new ArrayList<>(10);
        // 使用另外一个 ArrayList对其初始化
        List<Integer> list3 = new ArrayList<>(list2);

		list1.add(1);
        list1.add(2);
        list1.add(3);
        // 其父类 AbstractCollection重写了 toString方法
        System.out.println(list1);// 输出 [1, 2, 3]

    }

2、ArrayList的遍历

1、遍历顺序表

2、for - each(实现了Iterable接口)

3、迭代器(实现了Iterable接口)

// 遍历顺序表
  for (int i = 0; i < list.size(); i++) {
      System.out.print(list.get(i));
  }
  // for - each 遍历
  for (String s : list) {
      System.out.print(s);
  }

  // 迭代器打印
  // 获取迭代器对象
  Iterator<String> iterator = list.iterator();
  while (iterator.hasNext()) {
      // 获取下一个对象
      String next =  iterator.next();
      // 打印
      System.out.print(next);
  }
  // listIterator ---- 实现了 Iterator 接口
  ListIterator<String> iterator2 = list.listIterator();
   while (iterator2.hasNext()) {
       String next =  iterator2.next();
       System.out.print(next);
   }

这里的 listIterator 实现了 Iterator 接口,从方法上,listIterator 有更多的功能(方法),例如在遍历的时候,进行添加元素 add()。

ListIterator<String> iterator2 = list.listIterator();
while (iterator2.hasNext()) {
    String next =  iterator2.next();
    if (next.equals("hello")) {
        iterator2.add("三团");// 在 hello 的后面添加 三团
    }else{
        System.out.print(next + " ");
    }
}
System.out.println(list);// [hello, 三团, bit, world]

3、ArrayList的常见操作(方法)

方法 解释
boolean add(E e) 尾插 e
void add(int index, E element) 将 e 插入到 index 位置
boolean addAll(Collection<? extends E> c) 将集合 c 中的元素 尾插到该集合中
E remove(int index) 删除 index 位置元素并返回
boolean remove(Object o) 删除遇到的第一个 o
E get(int index) 获取下标 index 位置元素
E set(int index, E element) 将下标 index 位置元素设置为 element
void clear() 清空顺序表
boolean contains(Object o) 判断 o 是否在线性表中
int indexOf(Object o) 返回第一个 o 所在下标
int lastIndexOf(Object o) 返回最后一个 o 的下标
List< E > subList(int fromIndex, int toIndex) 截取部分 list
	List<String> list = new ArrayList<>();
	List<String> listAdd = new ArrayList<>();
	listAdd.add("hello");
	listAdd.add("world");
	listAdd.add("你好~");

	list.add("哈哈");// 尾插元素
	list.add(0,"你好"); // 0 下标插入 "你好 "
	list.addAll(listAdd);// 将集合 listAdd 中的元素尾插到该集合中

	String s = list.remove(0);// 删除 index 位置元素并返回
	boolean s2 = list.remove("hello");// 删除遇到的第一个 hello,没找到则返回 false

	list.set(0,"we");

	list.indexOf("we");//返回第一个 "we" 所在下标
	list.lastIndexOf("we");// 返回最后一个 "we" 的下标

	System.out.println(list);
	// 截取子串 -- 左闭右开区间
	List<String> sub = list.subList(1, 3);
	System.out.println(sub);
	list.set(2,"修改后的list");
	System.out.println(sub);

注意: 这里的 subList方法,并不是真正的返回一个截取部分的新地址,而是将原地址的截取部分返回,所以当修改原来的线性表中的元素时,子串中的内容也会发生改变。

4、ArrayList的扩容机制

1、当调用无参构造时,即List< String > list = new ArrayList<>(),底层还没有分配真正的内存(初始化是一个空数组),初始容量为 0。当第一次添加元素(调用 add 方法) 时,整个顺序表的容量被扩充为10,放满后,以 1.5 倍扩容。

2、当调用带容量的构造方法时,例如 List< String > list = new ArrayList<>(16),顺序表初始容量就为16,放满后以 1.5 倍扩容。

结论

如果调用无参构造方法,顺序表初始大小为0,当第一次放入元素时,整个顺序表容量变为10,当放满10个元素,进行1.5倍扩容。

如果调用给定容量的构造方法,初始大小就是给定的容量,当放满了,就进行1.5倍扩容。

三、模拟实现一个顺序表(Object[])

public class MyArrayList<E> {

    private Object[] elementData;// 数组
    private int usedSize;// 代表有效的数据个数
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    public MyArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    public MyArrayList(int capacity) {
        // 判断参数的合法性
        if (capacity >= 0) {
            this.elementData = new Object[capacity];
        }else {
            throw new RuntimeException("初始化的容量不能为负数!");
        }
    }

    /**
     * 添加元素
     * @param e 要添加的元素
     */
    public boolean add(E e) {
        // 确定一个真正的容量,预测 -> 如需扩容则扩容
        ensureCapacityInternal(usedSize + 1);
        // 扩容完毕,放数据
        elementData[usedSize++] = e;
        return true;
    }

    /**
     * 给 index位置添加元素
     * @param index
     * @param e
     */
    public boolean add(int index, E e) {
        // 检查 index 是否合法
        rangeCheckForAdd(index);
        // 确定一个真正的容量 -> 如需扩容则扩容
        ensureExplicitCapacity(usedSize + 1);
        // 移动 index后面的元素,并在 index位置插入元素
        copy(index,e);
        usedSize++;
        return true;
    }
    private void copy(int index, E e){
        for (int i = usedSize; i > index; i--) {
            elementData[i] = elementData[i - 1];
        }
        elementData[index] = e;
    }
    private void rangeCheckForAdd(int index) {
        if (index > usedSize || index < 0)
            throw new IndexOutOfBoundsException("index位置不合法!");
    }

    public void ensureCapacityInternal(int minCapacity) {
        // 计算出需要的容量
        int capacity = calculateCapacity(elementData, minCapacity);
        // 根据计算出的容量,看是否需要扩容或者分配内存
        ensureExplicitCapacity(capacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        // 如果需要的容量大于数组容量,就扩容
        if (minCapacity - elementData.length > 0)
            // 扩容
            grow(minCapacity);
    }

    private static final int MAX_ARRAY_SIZE  = Integer.MAX_VALUE - 8;
    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);;
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 确定之前数组是否分配过内存,没有的话返回一个初始化的容量 10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(10, minCapacity);
        }
        // 分配后,返回 +1 后的值,即实际所需要的容量
        return minCapacity;
    }
}

到此这篇关于Java 数据结构深入理解ArrayList与顺序表的文章就介绍到这了,更多相关Java ArrayList内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中ArrayList与顺序表的概念与使用实例

    目录 前言 泛型(Generic) 泛型的引入 泛型的基本概念 包装类(Wrapper Class) 包装类的引入 基本数据类型与包装类的对应关系 ArrayList与顺序表 ArrayList简介 ArrayList使用 ArrayList的构造 ArrayList常见方法 ArrayList的遍历 总结 前言 通过前面的博客我们已经大致了解了关于Java的基本知识,而下面的几篇博客我们着重开始对于数据结构的知识进行学习,这篇博客我们就了解关于顺序表和ArrayList的相关知识,从名字上我们

  • Java中Arraylist的最大长度

    目录 Arraylist的最大长度 Arraylist的MAX_ARRAY_SIZE=Integer.MAX_VALUE-8; Arraylist的最大长度为2147483647即2^31-1 ArrayList的扩容问题 ArrayList的容量有两种 1.无参的构造方法 2.含参的构造方法 Arraylist的最大长度 Arraylist的MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 最近在学习java的基础知识,学到集合的时候,在查看ArrayList的源

  • Java 精炼解读数据结构的顺序表如何操作

    目录 前言 一.什么是顺序表 顺序表的概念及结构 创建顺序表 获取顺序表长度 在pos位置新增元素 判定是否包含某个元素 查找某个元素对应的位置 获取pos位置的元素 给pos位置的元素设为value 删除你想要删除的元素 总结: 前言 线性表(linear list)是n个具有相同特性的数据元素的有限序列. 线性表是一种在实际中广泛使用的数据结构,常见 的线性表:顺序表.链表.栈.队列.字符串... 线性表在逻辑上是线性结构,也就说是连续的一条直线.但是在物理结构上并不一定是连续的,线性表在物

  • Java中ArrayList同步的2种方法分享

    目录 方法一:使用Collections.synchronizedList()方法 方法2:使用CopyOnWriteArrayList 向量同步时为什么要使用arrayList? 前言: arrayList 的实现是默认不同步的.这意味着如果一个线程在结构上修改它并且多个线程同时访问它,它必须在外部同步.结构修改意味着从列表中添加或删除元素或显式调整后备数组的大小.改变现有元素的值不是结构修改. 有两种方法可以创建同步Arraylist: 1. Collections.synchronized

  • Java ArrayList实现班级信息管理系统

    ArrayList实现班级信息管理系统,供大家参考,具体内容如下 代码如下: import java.util.*; public class Demo1 {     public static void main(String[] args) {         Manage M = new Manage();         Scanner reader = new Scanner(System.in);         while (true) {             System.o

  • Java实现顺序表的操作

    本文实例为大家分享了Java实现顺序表的基本操作,供大家参考,具体内容如下 静态顺序表:使用定长数组存储.动态顺序表:使用动态开辟的数组存储. 接口 package com.github.sqlist; public interface ISequence {     // 在 pos 位置插入 val     boolean add(int pos, Object data);     // 查找关键字 key 找到返回 key 的下表,没有返回 -1     int search(Objec

  • Java面试必备之ArrayList陷阱解析

    目录 问题分析 疑惑满满 拨云见日 回顾整个过程 如何正确的删除 总结 问题分析 疑惑满满 小枫听到这个面试题的时候,心想这是什么水面试官,怎么问这么简单的题目,心想一个for循环加上equal判断再删除不就完事了吗?但是转念一想,不对,这里面肯定有陷阱,不然不会问这么看似简单的问题.小枫突然想起来之前写代码的时候好像遇到过这个问题,也是在ArrayList中删除指定元素,但是直接for循环remove元素的时候还抛出了异常,面试官的陷阱估计在这里.小枫暗自窃喜,找到了面试官埋下的陷阱. 小枫回

  • Java中的ArrayList类常用方法和遍历

    目录 ArrayList类常用方法和遍历 常用的方法有 ArrayList类方法总结 关于ArrayList 常用方法总结 构建ArrayList 添加元素 删除元素 查找元素 获取元素 获取ArrayList数组长度 检查是否为空 遍历ArrayList ArrayList类常用方法和遍历 ArrayList类对于元素的操作,基本体现在——增.删.查. 常用的方法有 public boolean add(E e):将指定的元素添加到此集合的尾部. public E remove(int ind

  • Java 数据结构深入理解ArrayList与顺序表

    目录 一.ArrayList简介 二.ArrayList的使用 1.ArrayList的构造 2.ArrayList的遍历 3.ArrayList的常见操作(方法) 4.ArrayList的扩容机制 三.模拟实现一个顺序表(Object[]) 一.ArrayList简介 在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下: ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表. 二.ArrayList的使用 1.ArrayList的构造

  • Java超详细讲解ArrayList与顺序表的用法

    目录 简要介绍 Arraylist容器类的使用 Arraylist容器类的构造 ArrayList的常见方法 ArrayList的遍历 ArrayList中的扩容机制 简要介绍 顺序表是一段物理地址连续的储存空间,一般情况下用数组储存,并在数组上完成增删查改.而在java中我们有ArrayList这个容器类封装了顺序表的方法. 在集合框架中,ArrayList是一个普通的类,其实现了list接口.其源码类定义如图 可见,其实现了RandomAccess, Cloneable, 以及Seriali

  • Java中ArrayList与顺序表的定义与实现方法

    目录 1.线性表 定义 特征 2.顺序表 定义 实现 打印数组 新增元素 判断是否包含某个元素 查找元素 获取pos位置的元素 更改pos位置的值 删除操作 获取顺序表长度 清空顺序表 3.ArrayList 简介: 使用 一些常见方法 ArrayList的遍历 总结 1.线性表 定义 线性表是最基本.最简单.也是最常用的一种数据结构.线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列. 常见的线性表:顺序表.链表.栈.队列... 线性表在逻辑上是

  • 详解Python数据结构与算法中的顺序表

    目录 0. 学习目标 1. 线性表的顺序存储结构 1.1 顺序表基本概念 1.2 顺序表的优缺点 1.3 动态顺序表 2. 顺序表的实现 2.1 顺序表的初始化 2.2 获取顺序表长度 2.3 读取指定位置元素 2.4 查找指定元素 2.5 在指定位置插入新元素 2.6 删除指定位置元素 2.7 其它一些有用的操作 3. 顺序表应用 3.1 顺序表应用示例 3.2 利用顺序表基本操作实现复杂操作 0. 学习目标 线性表在计算机中的表示可以采用多种方法,采用不同存储方法的线性表也有着不同的名称和特

  • Java数据结构彻底理解关于KMP算法

    大家好,前面的有一篇文章讲了子序列和全排列问题,今天我们再来看一个比较有难度的问题.那就是大名鼎鼎的KMP算法. 本期文章源码:GitHub源码 简介 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法).KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息.KMP算法

  • C语言 数据结构之数组模拟实现顺序表流程详解

    目录 线性表和顺序表 线性表 顺序表 静态顺序表 动态顺序表 代码已经放在Gitee上,需要可以小伙伴可以去看看 用C语言数组模拟实现顺序表 Gitee 线性表和顺序表 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列,这是我们广泛使用的数据结构. 线性表在逻辑上是线性结构,也就说是连续的一条直线.但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储. 常见的线性表:顺序表.链表.栈.队列.字符串- 顺序表 顺序表是用一段物理地址连

  • java数据结构和算法中哈希表知识点详解

    树的结构说得差不多了,现在我们来说说一种数据结构叫做哈希表(hash table),哈希表有是干什么用的呢?我们知道树的操作的时间复杂度通常为O(logN),那有没有更快的数据结构?当然有,那就是哈希表: 1.哈希表简介 哈希表(hash table)是一种数据结构,提供很快速的插入和查找操作(有的时候甚至删除操作也是),时间复杂度为O(1),对比时间复杂度就可以知道哈希表比树的效率快得多,并且哈希表的实现也相对容易,然而没有任何一种数据结构是完美的,哈希表也是:哈希表最大的缺陷就是基于数组,因

  • 带你了解Java数据结构和算法之哈希表

    目录 1.哈希函数的引入 ①.把数字相加 ②.幂的连乘 2.冲突 3.开放地址法 ①.线性探测 ②.装填因子 ③.二次探测 ④.再哈希法 4.链地址法 5.桶 6.总结 1.哈希函数的引入 大家都用过字典,字典的优点是我们可以通过前面的目录快速定位到所要查找的单词.如果我们想把一本英文字典的每个单词,从 a 到 zyzzyva(这是牛津字典的最后一个单词),都写入计算机内存,以便快速读写,那么哈希表是个不错的选择. 这里我们将范围缩小点,比如想在内存中存储5000个英文单词.我们可能想到每个单词

  • Java数据结构顺序表用法详解

    目录 1.什么是顺序表 2.顺序表的基本功能和结构 3.顺序表基本功能的实现和解析 1.判断线性表是否为空 2.获取指定位置的元素 3.向线性表表添加元素 4.在位置i处插入元素 5.删除指定位置的元素,并返回该元素 6.查找t第一次出现的位置 7.手动扩容方法 1.什么是顺序表 在程序中,经常需要将一组(通常是同为某个类型的)数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等.一组数据中包含的元素个数可能发生变化(可以增加或删除元素). 对于这种需求,最简单的解决方

随机推荐