简单介绍:
ArrayList是Java中最常用的数据结构之一,底层是以数组(Object类型)的形式储存数据。它是一个动态数组,可以自动调整其大小以容纳更多元素实现了List接口,因此具有List接口的所有功能。
ArrayList是基于索引的数据结构,它可以快速地访问任何一个元素,因为它使用索引值来直接访问元素,而不是像链表那样需要遍历元素。这使得ArrayList在频繁读取操作的场景下非常高效。
ArrayList的特点如下:
-
动态调整大小:当添加的元素超过了当前ArrayList的容量时,它会自动扩容以容纳更多的元素。
-
随机访问:由于ArrayList是基于数组实现的,所以可以通过索引值直接访问任意位置的元素,具有很高的读取性能。
-
插入和删除效率低:由于需要进行元素的移动,所以在中间位置插入或删除元素的效率相对较低。
-
允许重复元素:ArrayList允许存储重复的元素,即同一个值可以出现在多个位置。
-
线程不安全:ArrayList不是线程安全的,如果需要在多线程环境下使用,需要通过外部同步机制来确保线程安全。
-
灵活的存储类型:虽然ArrayList通常用于存储同一种类型的元素,但它实际上可以接受任何类型的对象,包括原始数据类型。
在使用ArrayList时,需要注意以下几点:
- 初始容量:在创建ArrayList时,可以通过构造函数指定其初始容量,这有助于减少后续的扩容操作,提高性能。
- 扩容机制:当添加的元素数量超过当前容量时,ArrayList会自动扩容,通常是将容量翻倍。
- 泛型:为了提高类型安全性,建议使用泛型来声明ArrayList,这样可以在编译时检查类型错误。
- 遍历方式:可以使用for循环、迭代器或增强for循环来遍历ArrayList中的元素。
总的来说,ArrayList是一种功能强大且常用的数据结构,适用于需要频繁读取元素的场景。
ArrayList方法:
ArrayList实现了和List接口,这意味着它可以进行集合操作,如添加、删除和检索元素。
下面是List方法关于ArrayList的使用
add(E e):将指定的元素添加到列表的尾部。
add(int index, E element):在列表的指定位置插入指定元素,并移动当前位置及以下的元素。
addAll(Collection c):将指定集合中的所有元素添加到列表的尾部。
addAll(int index, Collection c):从指定位置开始,将指定集合中的所有元素插入列表中。
remove(Object o):移除列表中第一次出现的指定元素(如果存在)。
remove(int index):移除列表中指定位置的元素,并返回被移除的元素。
get(int index):返回列表中指定位置的元素。
set(int index, E element):用指定的元素替换列表中指定位置的元素。
indexOf(Object o):返回此列表中首次出现的指定元素的索引,或如果列表不包含元素,则返回 -1。
lastIndexOf(Object o):返回此列表中最后出现的指定元素的索引,或如果列表不包含元素,则返回 -1。
subList(int fromIndex, int toIndex):返回列表中指定的 fromIndex(包括)和 toIndex(不包括)之间的视图。
此外还有ArrayList自己实现的一些实用性的方法:
clear()用于清除列表
isEmpty()用于检查列表是否为空
size()用于获取列表的大小
iterator()用于获取列表的迭代器
ArrayList的三种构造器
1:无参构造器
//默认大小是10
源码:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
构建:
可见初始是一个空数组
2:.带单个整型参数的构造器(可以初始数组的大小)
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);
}
}
这种构造器允许我们指定ArrayList的初始容量。这在我们知道将要存储大量元素且希望提高性能时非常有用,因为它可以减少数组动态扩容的次数。
3:带有Collection参数的构造器
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;
}
}
例子:
public static void main(String[] args) {
ArrayList<Integer> arrayList1 = new ArrayList<>();
ArrayList<Number> arrayList2 = new ArrayList<>(arrayList1);
}
解释说明:对public ArrayList(Collection<? extends E> c)中的Collection<? extends E> c 分析将其分为两部分看待
1:Collection c
c的类型为Collection,也就是说传入的参数类型必须是Collection或其子类,而arrayList1的类型正是ArrayList为Collection的子类(下面是继承关系图)
2:<? extends E> c)
那例子讲解arrayList1的泛型为Integer,arrayList2的泛型为Number也就是说在构建arrayList2对象时《E》便是《Number》所以传入的参数arrayList1的泛型类型继承了E,则arrayList1的泛型,必须是E(Number)或其子类(Integer)。
带有Collection参数的构造器的作用:
创建一个ArrayList,并将传入的集合中的元素复制到这个新的ArrayList中**。这个构造器的使用场景包括但不限于:
- 从一个集合到另一个集合的数据迁移:当你需要将一个集合中的数据转移到一个新的ArrayList时,这个构造器非常有用。它允许你保持原有集合不变,同时在新的ArrayList中进行操作,这在需要对集合数据进行独立处理时尤其有用。
- 集合的深拷贝:使用这个构造器可以创建一个包含原集合所有元素的新列表,这是一种深拷贝。这意味着对新列表的修改不会影响到原始集合,反之亦然。
- 初始化具有初始元素的ArrayList:如果你有一个现有的集合,并且想要创建一个ArrayList,其中包含这些初始元素,这个构造器可以简化这个过程。
需要注意的是,这个构造器内部使用了toArray()
方法来将集合转换为数组,然后再将数组中的元素添加到ArrayList中。这种方法比直接添加每个元素更高效,因为它减少了迭代次数。此外,如果传入的集合本身是ArrayList,那么这个构造器会利用ArrayList的elementData
数组属性来存储数据,这样做可以避免额外的数组创建和数据复制。
在实际使用中,这个构造器提供了一种方便的方式来创建和初始化ArrayList,尤其是当你已经有了一个集合,并且希望在ArrayList中保留或处理这些数据时。
举个栗子:从一个集合到另一个集合的数据迁移*
public static void main(String[] args) {
ArrayList<Integer> arrayList1 = new ArrayList<>();
arrayList1.add(1);
arrayList1.add(2);
arrayList1.add(3);
ArrayList<Number> arrayList2 = new ArrayList<>(arrayList1);
arrayList2.add(4);
System.out.println(arrayList1);
System.out.println(arrayList2);
arrayList1.set(0,666);
System.out.println(arrayList1);
System.out.println(arrayList2);
}
分析:为浅拷贝所以修改arrayList1时arrayList2并不会改变
注意切割方法:subList(int fromIndex, int toIndex)
想想输出结果是什么?
public static void main(String[] args) {
ArrayList<Integer> arrayList1 = new ArrayList<>();
arrayList1.add(1);
arrayList1.add(2);
arrayList1.add(3);
arrayList1.add(4);
List<Integer> integers = arrayList1.subList(0, 2);
System.out.println(arrayList1);
System.out.println(integers);
System.out.println("==========");
arrayList1.set(0,666);
System.out.println(arrayList1);
System.out.println(integers);
}
切割方法是返回数据的地址所以arrayList1中的数据变了导致切割出的数据跟着变化了。
三种遍历方法
public static void main(String[] args) {
ArrayList<Integer> arrayList1 = new ArrayList<>();
arrayList1.add(1);
arrayList1.add(2);
arrayList1.add(3);
arrayList1.add(4);
//1:直接打印
System.out.println(arrayList1);
//2:普通for循环
for (int i = 0; i < arrayList1.size(); i++) {
System.out.print(arrayList1.get(i));
}
System.out.println();
//3:for -each
for (Integer x:arrayList1){
System.out.print(x);
}
System.out.println();
//4:迭代器
Iterator<Integer> iterator = arrayList1.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next());
}
System.out.println();
}
迭代器
在Java中,迭代器(Iterator)是一个对象,它的工作是遍历并选择序列中的对象,用于以标准方式遍历任何集合对象。
删除元素:除了遍历元素外,迭代器还提供了一个remove()方法,我们可以使用这个方法来删除当前元素。需要注意的是,我们不能在没有调用next()方法的情况下直接调用remove()方法,否则会抛出IllegalStateException异常。
代码演示
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
Iterator<Integer> iterator = numbers.iterator();
// 使用迭代器遍历并删除偶数元素
while (iterator.hasNext()) {
int number = iterator.next();
if (number % 2 == 0) {
iterator.remove(); // 删除当前元素
}
}
// 输出修改后的ArrayList
System.out.println(numbers);
}
}
总的来说,迭代器提供了一种高效且灵活的方式来遍历和修改集合中的元素。在处理大型集合时,使用迭代器可以大大提高代码的效率和可读性。
迭代器的遍历解析
1.可以将迭代器比作一个指针,原本指向集合的第一个位置
Iterator<Integer> iterator = arrayList1.iterator();//得到一个迭代器
while (iterator.hasNext()){//该步骤是判断是否存在数据
System.out.print(iterator.next());//这一步是将迭代器指向的数据打印,同时将迭代器指向下一个数据的位置
}
2.针对于ArrayList还有其特有的迭代器ListIterator是Iterator的子类有
ListIterator相较于Iterator具有更高级的功能,主要体现在遍历方向、添加元素和索引定位上。以下是它们之间的主要区别:
- 遍历方向:Iterator只能单向向前遍历集合,而ListIterator则可以进行双向遍历,即可以向前也可以向后遍历集合。
- 添加元素:ListIterator提供了add方法,允许在遍历过程中向List中添加对象,这是Iterator所不具备的功能。
- 索引定位:ListIterator可以获取当前遍历的索引位置,通过nextIndex()和previousIndex()方法来实现,这为操作集合提供了更多的灵活性。Iterator则没有提供这样的功能。
总的来说,ListIterator提供了比Iterator更为丰富的功能,特别是在需要双向遍历和修改列表时。然而,如果只需要单向遍历并且不需要修改集合,使用Iterator就足够了。在选择使用哪种迭代器时,应根据具体需求和集合类型来决定。
代码演示:
反向遍历
public static void main(String[] args) {
// 创建一个ArrayList
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 使用ListIterator遍历集合
System.out.println("使用ListIterator遍历集合:");
ListIterator<Integer> listIterator = list.listIterator();
while (listIterator.hasNext()) {
Integer element = listIterator.next();
System.out.print(element);
}
System.out.println();
// 使用ListIterator反向遍历集合
System.out.println("使用ListIterator反向遍历集合:");
while (listIterator.hasPrevious()) {
Integer element = listIterator.previous();
System.out.print(element);
}
}
add方法
public static void main(String[] args) {
// 创建一个ArrayList
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 使用ListIterator遍历集合并添加元素
System.out.println("原始列表:" + list);
ListIterator<Integer> listIterator = list.listIterator();
while (listIterator.hasNext()) {
Integer element = listIterator.next();
if (element.equals(2)) {
listIterator.add(6); // 在2之后添加6
}
}
System.out.println(list);
}