Java数据结构之ArrayList从顺序表到实现

 更新时间:2023年04月28日 08:30:12   作者:ZIYE_190  
Java中的ArrayList是一种基于数组实现的数据结构,支持动态扩容和随机访问元素,可用于实现顺序表等数据结构。ArrayList在内存中连续存储元素,支持快速的随机访问和遍历。通过学习ArrayList的实现原理和使用方法,可以更好地掌握Java中的数据结构和算法

1 ArrayList

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

说明:

  • ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
  • ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
  • ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
  • 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
  • ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

2 ArrayList使用

2.1 ArrayList的构造

方法解释
ArrayList()无参构造
ArrayList(Collection<? extends E> c)利用其他 Collection 构建 ArrayList
ArrayList(int initialCapacity)指定顺序表初始容量
public static void main(String[] args) {
	// ArrayList创建,推荐写法
	// 构造一个空的列表
	List<Integer> list1 = new ArrayList<>();
	// 构造一个具有10个容量的列表
	List<Integer> list2 = new ArrayList<>(10);
	list2.add(1);
	list2.add(2);
	list2.add(3);
	// list2.add("hello"); // 编译失败,List<Integer>已经限定了,list2中只能存储整形元素
	// list3构造好之后,与list中的元素一致
	ArrayList<Integer> list3 = new ArrayList<>(list2);
	// 避免省略类型,否则:任意类型的元素都可以存放,使用时将是一场灾难
	List list4 = new ArrayList();
	list4.add("111");
	list4.add(100);
}

2.2 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 subList(int fromIndex, int toIndex)截取部分 list
public static void main(String[] args) {
	List<String> list = new ArrayList<>();
	list.add("JavaSE");
	list.add("JavaWeb");
	list.add("JavaEE");
	list.add("JVM");
	list.add("测试课程");
	System.out.println(list);
	// 获取list中有效元素个数
	System.out.println(list.size());
	// 获取和设置index位置上的元素,注意index必须介于[0, size)间
	System.out.println(list.get(1));
	list.set(1, "JavaWEB");
	System.out.println(list.get(1));
	// 在list的index位置插入指定元素,index及后续的元素统一往后搬移一个位置
	list.add(1, "Java数据结构");
	System.out.println(list);
	// 删除指定元素,找到了就删除,该元素之后的元素统一往前搬移一个位置
	list.remove("JVM");
	System.out.println(list);
	// 删除list中index位置上的元素,注意index不要超过list中有效元素个数,否则会抛出下标越界异常
	list.remove(list.size()-1);
	System.out.println(list);

//输出结果:
[JavaSE, JavaWeb, JavaEE, JVM, 测试课程]
5
JavaWeb
JavaWEB
[JavaSE, Java数据结构, JavaWEB, JavaEE, JVM, 测试课程]
[JavaSE, Java数据结构, JavaWEB, JavaEE, 测试课程]
[JavaSE, Java数据结构, JavaWEB, JavaEE]

2.3 ArrayList的遍历

ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器

public static void main(String[] args) {
	List<Integer> list = new ArrayList<>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(5);
	// 使用下标+for遍历
	for (int i = 0; i < list.size(); i++) {
		System.out.print(list.get(i) + " ");
	} 
	System.out.println();
	// 借助foreach遍历
	for (Integer integer : list) {
		System.out.print(integer + " ");
	} 
	System.out.println();
	Iterator<Integer> it = list.listIterator();
	while(it.hasNext()){
	System.out.print(it.next() + " ");
	} 
	System.out.println();
}

//输出结果:
1 2 3 4 5 
1 2 3 4 5 
1 2 3 4 5 

2.4 ArrayList的扩容机制

ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容:以下是ArrayList源码中扩容方式

Object[] elementData; // 存放元素的空间
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间
private static final int DEFAULT_CAPACITY = 10; // 默认容量大小
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++;
	// overflow-conscious code
	if (minCapacity - elementData.length > 0)
		grow(minCapacity);
	}
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	private void grow(int minCapacity) {
	// 获取旧空间大小
	int oldCapacity = elementData.length;
	// 预计按照1.5倍方式扩容
	int newCapacity = oldCapacity + (oldCapacity >> 1);
	// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
	if (newCapacity - minCapacity < 0)
	newCapacity = minCapacity;
	// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
	if (newCapacity - MAX_ARRAY_SIZE > 0)
	newCapacity = hugeCapacity(minCapacity);
	// 调用copyOf扩容
	elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
	// 如果minCapacity小于0,抛出OutOfMemoryError异常
	if (minCapacity < 0)
	throw new OutOfMemoryError();
	return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

检测是否真正需要扩容,如果是调用grow准备扩容

预估需要库容的大小

  • 初步预估按照1.5倍大小扩容
  • 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
  • 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

使用copyOf进行扩容

3 使用示例

扑克牌:

public class Card {
	public int rank; // 牌面值
	public String suit; // 花色
	@Override
	public String toString() {
		return String.format("[%s %d]", suit, rank);
	}
}
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
public class CardDemo {
	public static final String[] SUITS = {"♠", "♥", "♣", "♦"};
	// 买一副牌
	private static List<Card> buyDeck() {
		List<Card> deck = new ArrayList<>(52);
		for (int i = 0; i < 4; i++) {
			for (int j = 1; j <= 13; j++) {
				String suit = SUITS[i];
				int rank = j;
				Card card = new Card();
				card.rank = rank;
				card.suit = suit;
				deck.add(card);
			}
		} 
		return deck;
	}
	private static void swap(List<Card> deck, int i, int j) {
		Card t = deck.get(i);
		deck.set(i, deck.get(j));
		deck.set(j, t);
	}
	private static void shuffle(List<Card> deck) {
		Random random = new Random(20190905);
		for (int i = deck.size() - 1; i > 0; i--) {
			int r = random.nextInt(i);
			swap(deck, i, r);
		}
	}
	public static void main(String[] args) {
		List<Card> deck = buyDeck();
		System.out.println("刚买回来的牌:");
		System.out.println(deck);
		shuffle(deck);
		System.out.println("洗过的牌:");
		System.out.println(deck);
		// 三个人,每个人轮流抓 5 张牌
		List<List<Card>> hands = new ArrayList<>();
		hands.add(new ArrayList<>());
		hands.add(new ArrayList<>());
		hands.add(new ArrayList<>());
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 3; j++) {
				hands.get(j).add(deck.remove(0));
			}
		}
 		System.out.println("剩余的牌:");
		System.out.println(deck);
		System.out.println("A 手中的牌:");
		System.out.println(hands.get(0));
		System.out.println("B 手中的牌:");
		System.out.println(hands.get(1));
		System.out.println("C 手中的牌:");
		System.out.println(hands.get(2));
	}
}

运行结果

刚买回来的牌:
[[♠ 1], [♠ 2], [♠ 3], [♠ 4], [♠ 5], [♠ 6], [♠ 7], [♠ 8], [♠ 9], [♠ 10], [♠ 11], [♠ 12], [♠ 13], [♥ 1], [♥ 2], [♥ 3], [♥ 4], [♥ 5], [♥ 6], [♥ 7],
[♥ 8], [♥ 9], [♥ 10], [♥ 11], [♥ 12], [♥ 13], [♣ 1], [♣ 2], [♣ 3], [♣ 4], [♣ 5], [♣ 6], [♣ 7], [♣ 8], [♣ 9], [♣ 10], [♣ 11], [♣ 12], [♣
13], [♦ 1], [♦ 2], [♦ 3], [♦ 4], [♦ 5], [♦ 6], [♦ 7], [♦ 8], [♦ 9], [♦ 10], [♦ 11], [♦ 12], [♦ 13]]
洗过的牌:
[[♥ 11], [♥ 6], [♣ 13], [♣ 10], [♥ 13], [♠ 2], [♦ 1], [♥ 9], [♥ 12], [♦ 5], [♥ 8], [♠ 6], [♠ 3], [♥ 5], [♥ 1], [♦ 6], [♦ 13], [♣ 12], [♦ 12],
[♣ 5], [♠ 4], [♣ 3], [♥ 7], [♦ 3], [♣ 2], [♠ 1], [♦ 2], [♥ 4], [♦ 8], [♠ 10], [♦ 11], [♥ 10], [♦ 7], [♣ 9], [♦ 4], [♣ 8], [♣ 7], [♠ 8], [♦ 9], [♠
12], [♠ 11], [♣ 11], [♦ 10], [♠ 5], [♠ 13], [♠ 9], [♠ 7], [♣ 6], [♣ 4], [♥ 2], [♣ 1], [♥ 3]]
剩余的牌:
[[♦ 6], [♦ 13], [♣ 12], [♦ 12], [♣ 5], [♠ 4], [♣ 3], [♥ 7], [♦ 3], [♣ 2], [♠ 1], [♦ 2], [♥ 4], [♦ 8], [♠ 10], [♦ 11], [♥ 10], [♦ 7], [♣ 9], [♦
4], [♣ 8], [♣ 7], [♠ 8], [♦ 9], [♠ 12], [♠ 11], [♣ 11], [♦ 10], [♠ 5], [♠ 13], [♠ 9], [♠ 7], [♣ 6], [♣ 4], [♥ 2], [♣ 1], [♥ 3]]
A 手中的牌:
[[♥ 11], [♣ 10], [♦ 1], [♦ 5], [♠ 3]]
B 手中的牌:
[[♥ 6], [♥ 13], [♥ 9], [♥ 8], [♥ 5]]
C 手中的牌:
[[♣ 13], [♠ 2], [♥ 12], [♠ 6], [♥ 1]]

到此这篇关于Java数据结构之ArrayList从顺序表到实现的文章就介绍到这了,更多相关Java ArrayList内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot整合Mybatis-plus的实现

    springboot整合Mybatis-plus的实现

    这篇文章主要介绍了springboot整合Mybatis-plus的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Java中instanceof关键字实例讲解

    Java中instanceof关键字实例讲解

    大家好,本篇文章主要讲的是Java中instanceof关键字实例讲解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • IntelliJ IDEA 2022.1.1创建java项目的详细方法步骤

    IntelliJ IDEA 2022.1.1创建java项目的详细方法步骤

    最近安装了IntelliJ IDEA 2022.1.1,发现新版本的窗口还有些变化的,所以下面这篇文章主要给大家介绍了关于IntelliJ IDEA 2022.1.1创建java项目的详细方法步骤,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • java枚举的使用示例

    java枚举的使用示例

    我们在学习编程语言的时候都学过枚举,现在就具体来看看java中的枚举的使用
    2013-12-12
  • 利用Spring Validation实现输入验证功能

    利用Spring Validation实现输入验证功能

    这篇文章主要给大家介绍了如何利用Spring Validation完美的实现输入验证功能,文中有详细的代码示例,具有一定的参考价值,感兴趣的朋友可以借鉴一下
    2023-06-06
  • Java的Synchronized关键字学习指南(全面 & 详细)

    Java的Synchronized关键字学习指南(全面 & 详细)

    这篇文章主要给大家介绍了关于Java的Synchronized关键字的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java环境下高德地图Api的使用方式

    Java环境下高德地图Api的使用方式

    这篇文章主要介绍了Java环境下高德地图Api的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java之Runnable启动线程的使用方式

    Java之Runnable启动线程的使用方式

    这篇文章主要介绍了Java之Runnable启动线程的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 如何用JAVA判断当前时间是否为节假日、周末、工作日及调休日(不报错:IOException!)

    如何用JAVA判断当前时间是否为节假日、周末、工作日及调休日(不报错:IOException!)

    最近公司有个业务需要判断工作日,但是每年的节假日不一样,下面这篇文章主要给大家介绍了关于如何用JAVA判断当前时间是否为节假日、周末、工作日及调休日的相关资料,且不报错:IOException!,需要的朋友可以参考下
    2023-12-12
  • 微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计功能

    微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计功能

    这篇文章主要介绍了微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计功能,文章简单介绍了Redis BitMap 基本用法结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-01-01

最新评论