admin管理员组文章数量:1032785
ArrayList全面使用
Java 中的 ArrayList 是一个可调整大小的数组,可以在内存中增长或收缩。它是使用初始容量动态创建的。
这意味着如果超过数组的初始容量,将自动创建一个容量更大的新数组,并将当前数组中的所有元素复制到新数组中。
数组列表中的元素是从零索引开始放置的。也就是说,第一个元素将放置在 0 索引处,最后一个元素将放置在索引 (n-1) 处,其中 n 是 ArrayList 的大小。
Java ArrayList 在内部使用动态数组来存储元素或数据组。
ArrayList 的容量不会自动收缩。从列表中删除元素时,数组列表的大小可以自动缩小,但不能缩小容量。
Java中ArrayList类的层次结构图
ArrayList 类的层次结构图如下图所示。
如上面的层次结构图所示,ArrayList 类实现了 List 接口并扩展了实现 List 接口的 AbstractList(抽象List)。
Java ArrayList 类还实现了 3 个标记接口:Random Access(随机访问), Cloneable(克隆), Serializable(可序列化)。
标记接口是没有任何方法或任何成员变量的接口。它也被称为空接口,因为没有字段或方法。
Random Access接口
1. Random Access接口是一个标记接口,不定义任何方法或成员。它是在 Java 1.4 版本中引入的,用于优化列表性能。
2. RandomAccess 接口存在于 java.util 包中。
3. ArrayList 类实现了一个随机访问接口,以便我们可以以相同的速度访问任何随机元素。例如,数组列表中有一亿个对象。第一个元素是 x,第 10 个元素是 y,第 1 亿个元素是 z。
现在假设第一个元素 x 可以在 1 秒内访问。由于实现了随机访问接口,第10个元素和第1亿个元素也可以在1秒内访问。
因此,我们可以以相同或恒定的速度访问任何随机元素。如果我们经常的操作是检索操作,那么 ArrayList 是最佳选择。 Serializable接口
1. 可序列化接口是用于通过网络发送对象组的标记接口。它存在于 java.io 包中。
2.它有助于将数据从一个类发送到另一个类。通常,我们使用集合来保存对象并将其从一个地方转移到另一个地方。
为了支持此要求,每个集合类都已实现可序列化和可克隆。
Cloneable接口
1. java.lang 包中存在一个Cloneable接口。
2.它用于创建完全重复的对象。当数据或对象组来自网络时,接收方将创建重复的对象。
创建完全重复的对象的过程称为克隆。这是集合类的一个非常常见的要求。
ArrayList 在 Java 中的功能
Java中ArrayList类有几个特性。它们如下:
1. 可调整大小的数组:ArrayList 是一个可调整大小的数组或可增长的数组,这意味着 ArrayList 的大小可以在运行时增加或减少大小。创建 ArrayList 后,我们可以添加任意数量的元素。
2.基于索引的结构:它在java中使用基于索引的结构。
3.重复元素:数组列表中允许重复元素。
4. 空元素:可以将任意数量的空元素添加到数组列表中。
5.插入顺序:它维护Java中的插入顺序。即插入顺序保持不变。
6. 异构对象:除TreeSet 和TreeMap外,其他都允许异构对象。异构意味着不同的元素。
7. 已同步:ArrayList未同步。这意味着多个线程可以同时使用相同的ArrayList对象。
8. 随机访问:ArrayList 实现了随机访问,因为它使用基于索引的结构。因此,我们可以从任意位置获取、设置、插入和删除数组列表的元素。
9. 性能:在 ArrayList 中,变更类的操作很慢。
例如,如果数组列表有 500 个元素,我们删除了第 50 个元素,那么第 51 个元素将尝试获取第 50 个位置,同样,所有元素也是如此。因此,它消耗了大量的时间来进行移动操作。
Java ArrayList Constructor
Java ArrayList 类提供了三个构造函数,用于创建 ArrayList 的对象。它们是:
- ArrayList()
- ArrayList(int initialCapacity)
- ArrayList(Collection c)
在Java中创建ArrayList类的对象非常简单。首先,我们将声明一个数组列表变量并调用数组列表构造函数来实例化 ArrayList 类的对象,然后将其分配给变量。
我们可以通过使用三个构造函数中的任何一个在 java 中创建 ArrayList 类的对象。让我们一起看看。
1. 创建 ArrayList 类实例的语法为:
代码语言:javascript代码运行次数:0运行复制ArrayList al = new ArrayList();
它创建一个空的 ArrayList,默认初始容量为 10。在这个数组列表中,我们只能存储 10 个元素,如下图所示。
假设我们将第 10 位的第 11 个元素插入到数组列表中,内部会发生什么?
一旦 ArrayList 达到其最大容量,ArrayList 类就会自动创建一个容量更大的新数组。
New capacity = (current capacity*3/2) + 1 = 10*3/2 + 1 = 16
创建容量更大的新数组列表后,所有现有元素都将复制到新数组列表中,然后将新元素添加到其中。将引用重新分配给新的数组对象,如上图所示。
包含对象集合的旧默认数组列表将自动进入垃圾回收。同样,如果我们尝试插入第 17 个元素,新容量 = 16*3/2 + 1 = 25。
2. 我们还可以在创建 ArrayList 对象时初始化容量。创建具有初始容量的 ArrayList 类实例的语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList al = new ArrayList(int initialCapacity);
它创建一个具有初始容量的空数组列表。如果知道初始容量,可以直接使用此方法;否则我们就应该采用默认值以提高系统的性能。
例如,假设我们的要求是在数组列表中添加 500 个元素,我们将创建如下的 ArrayList 对象:
代码语言:javascript代码运行次数:0运行复制ArrayList list2 = new ArrayList(500);
3. 我们还可以通过集合参数的方式来初始化ArrayList 类。语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList al = new ArrayList(Collection c);
它通过一个给定的集合 c 的元素来创建 ArrayList 对象。
例如:
代码语言:javascript代码运行次数:0运行复制ArrayList list3 = new ArrayList(list1); // list1 is elements of collection.
在 Java 中创建泛型数组列表对象
Java 1.5 或更高版本还提供了我们可以指定 ArrayList 对象中元素的类型方法。例如,我们可以创建一个的字符串数组泛型列表对象,如下所示:
代码语言:javascript代码运行次数:0运行复制 ArrayList<String> al = new ArrayList<String>(); // It can be used to store only String type.
// The advantage of specifying a type is that when we try to add another type of element, it will give compile-time error.
or,
Creating a Generic ArrayList object can also be done in separate lines like this:
ArrayList<String> arlist;
arlist = new ArrayList();
注意:我们不能使用原始数据类型作为类型。例如,ArrayList<int>是非法的。
Java ArrayList Initialization
在 Java 中初始化数组列表有三种方法。它们如下:
1. 使用 Arrays.asList:使用 asList() 方法初始化 ArrayList 的语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList<Type> list = new ArrayList<Type>(Arrays.asList(Object o1, Object o2, .. so on));
For example:
ArrayList<String> ar = new ArrayList<String>(Arrays.asList("A", "B", "C"))
2:使用普通方式:这是在java程序中初始化ArrayList的流行方法。初始化数组列表的语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList<Type> obj = new ArrayList<Type>();
obj.add("Obj o1");
obj.add("Obj o2");
and so on.
3. 使用匿名内部类:在java中使用匿名内部类初始化ArrayList的语法为:
代码语言:javascript代码运行次数:0运行复制ArrayList<Type> arl = new Arraylist<Type>() {{
add(Object o1);
add(Object o2);
add(Object o3);
. . . . . . . .
. . . . . . .
}};
Java 中的 ArrayList Methods
ArrayList还提供了一些有用的方法。它们如下:
1. boolean add(Object o):此方法用于在数组列表末尾添加一个元素。例如,如果你想在列表的末尾添加一个元素,你只需像这样调用 add() 方法:
代码语言:javascript代码运行次数:0运行复制list.add("Shubh"); // This will add "Shubh" at the end of the list.
2. boolean addAll(Collection c): 此方法用于在列表末尾添加特定集合中的一组元素。例如,假设我们在 list2 中有一组元素,并希望在 list1 的末尾添加,我们将像这样调用此方法:
代码语言:javascript代码运行次数:0运行复制 list1.addAll(list2);
3. boolean addAll(int index, Collection c):此方法用于在列表中的指定位置添加一组元素。例如:
代码语言:javascript代码运行次数:0运行复制list1.addAll(2, list2): // Adding all elements of list2 at position index 2 in list1
4. void add(int index, Object o):用于在列表中的特定位置索引处添加一个元素。例如:
代码语言:javascript代码运行次数:0运行复制list.add(3, "a"); // Adding element 'a' at position index 3 in list.
5. void addAll(int index, Object o):用于在列表中的特定位置添加特定元素。例如,假设我们想在列表中的位置 2 处放置一个特定的元素 “Shubh”,我们将调用 add(int index, Object o) 方法,如下所示:
代码语言:javascript代码运行次数:0运行复制list.add(2,"Shubh"); // This will add "Shubh" at the second position.
让我们以一个基于上述 ArrayList add() 方法的示例程序为例。
程序源代码 1:
代码语言:javascript代码运行次数:0运行复制package ArrayListTest;
import java.util.ArrayList;
public class AddExample
{
public static void main(String[] args)
{
// Create an object of the non-generic ArrayList.
ArrayList al = new ArrayList(); // list 1 with default capacity 10.
al.add("A");
al.add("B");
al.add(20);
al.add("A");
al.add(null);
System.out.println(al);
// Create an object of another non-generic ArrayList.
ArrayList al1 = new ArrayList(); // List 2.
al1.add("a");
al1.add("b");
al1.add("c");
// Call addAll(Collection c) method using reference variable al to add all elements at the end of the list1.
al.addAll(al1);
System.out.println(al);
// Call addAll(int index, Collection c) method using reference variable al1 to add all elements at specified position 2.
al1.addAll(2, al);
System.out.println(al1);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[A, B, 20, A, null]
[A, B, 20, A, null, a, b, c]
[a, b, A, B, 20, A, null, a, b, c, c]
在前面的示例程序中,我们使用了重复元素、异构元素(即字符串和整数)和空元素。您还会注意到,当在特定位置添加指定元素时,右侧元素会向右移动一个位置。
移位后,对象引用变量将重新分配一个新的数组列表,如上图所示。由于转移,ArrayList也很耗时。
在输出中,我们得到了这个数组列表的方括号表示法。这是因为当我们尝试打印对象引用变量时,它会在内部调用 toString() 方法,如下所示:
代码语言:javascript代码运行次数:0运行复制System.out.println(al); ------> System.out.println(al.toString());
5. void addAll(int index, Object o): 如果指定元素存在,它将从此列表中删除该元素的第一个匹配项。
6. void remove(int index):此方法从列表中的特定位置删除元素。请看下面的例子。
代码语言:javascript代码运行次数:0运行复制list.remove("A");
list.remove(2); // It will remove element from position 2.
7. void clear():clear() 方法用于从数组列表中删除所有元素。
8. void set(int index, Object o):set() 方法用指定的元素替换列表中特定位置的元素。例如,假设我们要将位置 2 的元素 “A” 替换为列表中的元素 “a”,我们将不得不将此方法调用为:
代码语言:javascript代码运行次数:0运行复制list.set(2, "a");
让我们举一个示例程序,我们将使用 remove() 方法从列表中删除一个元素。
程序源代码 2:
代码语言:javascript代码运行次数:0运行复制package ArrayListTest;
import java.util.ArrayList;
public class RemoveEx
{
public static void main(String[] args)
{
// Create a generic Arraylist object of String type.
// This means the compiler will show an error if we try to put any other element than String.
ArrayList<String> al = new ArrayList<String>(); // Default capacity is 10.
// Adding elements of String type.
al.add("A");
al.add("B");
al.add("C");
al.add("D");
al.add(null);
al.add("D");
System.out.println(al);
// Call remove() method to remove element D.
al.remove("D"); // removes the first occurrence of the specified element D at position 3, not from the position 5.
System.out.println(al);
al.remove(3);
System.out.println(al);
// Call set method to replace the element D with a null element at position 3.
al.set(3, null);
System.out.println(al);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[A, B, C, D, null, D]
[A, B, C, null, D]
[A, B, C, D]
[A, B, C, null]
在此示例程序中,您将观察到,当从数组中删除或删除元素时,已删除的元素变为 null,但已删除元素占用的空插槽仍保留在数组中。
数组右侧的任何后续元素都会自动移动到左侧的一个位置,以填充被已删除元素占用的空插槽,对象引用变量 'al' 将被重新分配给新的数组列表,如上图所示。
9. Object get(int index)::它返回此列表中指定位置的元素。
10. int size():它返回列表的元素数。大小 表示数组列表中存在的元素数。容量是指存储元素的能力。
11. Object get(int index):如果列表中存在指定的元素,则返回 true,否则返回 false。请看下面的例子。
代码语言:javascript代码运行次数:0运行复制String str = list.get(2);
int numberOfElements = list.size();
list.contains("A");
让我们看一个基于这些方法的示例程序。
程序源代码 3:
代码语言:javascript代码运行次数:0运行复制package arrayListPrograms;
import java.util.ArrayList;
public class ArrayListTest
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add("Apple");
al.add("Orange");
al.add("Banana");
al.add("Gauva");
System.out.println(al);
// Call get() method using object reference variable 'al' to get the specified element.
// Since return type of get() method is String. Therefore, we will store it by using a fruitsName variable with data type String.
String fruitsName = al.get(2);
System.out.println(fruitsName);
// Call size() method to get the number of elements present in the list.
// Since return type of size method is an integer. Therefore, we will store it by using variable numberOfElements with data type integer.
int numberOfElements = al.size();
System.out.println(numberOfElements);
// Check apple element is present or not.
boolean check = al.contains("Apple");
System.out.println(check);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[Apple, Orange, Banana, Gauva]
Banana
4
true
12. int indexOf(Object o):用于获取指定元素第一次出现的索引,如果列表中不存在该元素,则返回值 -1。
13. int lastIndexOf(Object o):用于获取列表中指定元素最后一次出现的索引。如果列表中不存在该元素,则返回 -1。例如:
代码语言:javascript代码运行次数:0运行复制int pos = list.indexOf("Apple");
int lastPos = list.lastIndexOf(20);
程序源代码 4:
代码语言:javascript代码运行次数:0运行复制package ArrayListTest;
import java.util.ArrayList;
public class Test
{
public static void main(String[] args)
{
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
System.out.println(list);
int pos = list.indexOf(30);
System.out.println(pos);
int lastPos = list.lastIndexOf(40);
System.out.println(lastPos);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[10, 20, 30, 40]
23
我们如何手动增加或减少 ArrayList 的当前容量?
1. ensureCapacity():此方法用于增加 ArrayList 的当前容量。由于当我们添加更多元素时,数组列表的容量会自动增加。但是要手动增加,请使用 ArrayList 类的 ensureCapacity() 方法。 2. trimTosize():trimTosize() 方法用于将 ArrayList 的容量修剪为 ArrayList 的当前大小。
代码语言:javascript代码运行次数:0运行复制ArrayList<String> list = new ArrayList<String>(); // Here, list can hold 10 elements.(Default initial capacity).
list.ensureCapacity(20); // Now it can hold 20 elements.
list.trimTosize();
如何在 Java 中同步 ArrayList
什么是 Java 中的同步?
从技术上讲,java 中的同步是一次只允许一个线程完全完成任务的过程。它只允许一个线程访问单个资源或单个任务。
当多个线程同时访问相同的资源时,程序可能会产生不是期望值的输出。因此,可以使用称为同步的技术来解决此问题。同步的目的是控制对共享资源的访问。
我们知道默认情况下 ArrayList 类不是线程安全。这意味着多个线程可以同时访问同一个 ArrayList 对象或实例。
因此,如果没有显式同步,它不能在多线程环境中被正确地使用。这是因为如果在多线程环境中使用 ArrayList 而不使用同步技术,那么它可能会产生不可预测的输出。
程序源代码 5:
代码语言:javascript代码运行次数:0运行复制package arrayListProgram;
import java.util.ArrayList;
public class MultiThreadEx extends Thread
{
// Declare ArrayList variable.
ArrayList<Integer> list;
// Create a one parameter constructor with parameter 'list' and type ArrayList.
MultiThreadEx(ArrayList<Integer> list)
{
this.list = list;
}
@Override
public void run()
{
System.out.println("Run method");
for(int i = 0 ; i < = 6; i++)
{
// Adding elements in the list.
list.add(i);
try {
// Call sleep() method to delay some time.
Thread.sleep(50);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
// Creates an ArrayList object with an initial capacity of 10.
ArrayList<Ineger> list = new ArrayList<Integer>();
// Create multiple thread class objects m1, m2 and passes 'list' as a parameter.
MultiThreadEx m1 = new MultiThreadEx(list);
MultiThreadEx m2 = new MultiThreadEx(list);
// Call start() method to start thread using m1 and m2 reference variable.
m1.start();
m2.start();
try {
// Call join() method to complete the task of adding elements in the ArrayList.
m1.join();
m2.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Integer sizelist = list.size();
System.out.println("Size of list is " + sizelist);
for(Integer i : list)
{
System.out.println("num - " + i);
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Run method
Run method
Size of list is 12
num - 0
num - 0
num - 1
num - 2
num - 2
num - 3
num - 3
num - 4
num - 4
num - 5
num - 5
num - 6
解释:
正如您在前面的示例程序中观察到的那样,ArrayList 实例在类对象 m1 和 m2 的两个线程之间共享。每个线程都尝试在 ArrayList 中添加 6 个元素。由于数组列表未同步。
因此,预期的输出是不可预测的。当我们运行上面的代码时,它显示 ArrayList 的大小是 12(而不是 14)。元素 1 和 6 在列表中仅插入一次。
让我们看看上面的程序发生了什么。
➤ 从主线程创建线程 1 时,它会在列表中插入元素 0。在列表中添加元素 0 后,线程 1 进入睡眠位置。
由于我们使用join()方法完成通过m1将元素从0添加到6的任务,所以当thread-1进入睡眠状态时,同时,主线程将启动thread-2执行,并将在列表中添加一个元素0。
➤ 当线程 1 的休眠时间结束时,线程 1 将再次进入运行状态,并在列表中添加元素 1。添加后,它将再次进入睡眠位置。
➤ 休眠时间结束后,它再次进入运行状态以插入元素 2。添加后,线程 1 将再次进入睡眠状态。同时,主线程将再次启动线程 2 以完成其任务并添加不可预测的元素 2。
正如您在程序中观察到的那样,在多线程环境中使用 ArrayList 并不能提供线程安全性。这是因为 ArrayList 未同步。
那么,我们将如何在Java中获得具有线程安全性的同步ArrayList呢?
有两种方法可以获取 ArrayList 的同步版本。它们如下:
- 通过使用 Collections.syncdList() 方法
- 通过使用 CopyOnWriteArrayList
使用 Collections.syncdList() 方法同步 ArrayList
此方法用于同步 Java 中的集合。此方法的语法如下:
语法:
代码语言:javascript代码运行次数:0运行复制public static List<T> synchronizedList(List<T> list)
1. syncdList() 方法接受 List,它可以是 List 接口的实现。例如,ArrayList 和 LinkedList。 2. 此方法的返回类型是同步列表(线程安全)。
3. 同步列表是方法的名称。
4. 参数列表是要包装在同步列表中的列表。
5. T 表示泛型的类型。
注意:迭代器在同步块中用于同步,以避免 Java 中的 ConcurrentModificationException。同步的 ArrayList 返回的迭代器是快速故障的。当在迭代期间列表中发生任何修改时,它将抛出 ConcurrentModificationException。
让我们举一个基本的例子来同步 java 中的 ArrayList。
代码语言:javascript代码运行次数:0运行复制ArrayList<String> al = new ArrayList<String>(); // Non-synchronized.
List l = Collections.synchronizedList( al );
l ➟ synchronized.
al ➟ Non-synchronized.
or,
List<String> synlist = Collections.synchronizedList( new ArrayList<String>);
同样,我们可以使用以下语法获取 Set, Map 对象的同步版本:
代码语言:javascript代码运行次数:0运行复制public static Set synchronizedList( Set s );
public static Map synchronizedList( Map m );
让我们举一个示例程序,其中我们将同步未同步的 ArrayList 对象列表,然后我们将调用 iterator() 方法来迭代同步的 ArrayList 对象列表。
程序源代码 1:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class ArrayListSynchronizationTest
{
public static void main(String[] args)
{
// Create an ArrayList object with initial capacity of 10.
// Non-synchronized ArrayList Object.
List<String> l = new ArrayList<String>();
// Add elements in the list.
l.add("Apple");
l.add("Orange");
l.add("Banana");
l.add("Pineapple");
l.add("Guava");
// Synchronizing ArrayList in Java.
List<String> synlist = Collections.synchronizedList( l ); // l is non-synchronized.
// Here, we will use a synchronized block to avoid the non-deterministic behavior.
synchronized(synlist)
{
// Call iterator() method to iterate the ArrayList.
Iterator<String> itr = synlist.iterator();
while(itr.hasNext())
{
String str = itr.next();
System.out.println(str);
}
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Apple Orange Banana Pineapple Guava
在前面的程序中,ArrayList 通过调用将整个列表锁定到同步块中的 syncdList() 方法来实现线程安全。 让我们以另一个程序为例,我们将通过在迭代期间向同步列表中添加一个数字来检查 ConcurrentModificationException 的出现。
程序源代码 2:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class CMETest
{
public static void main(String[] args)
{
ArrayList<Integer> al = new ArrayList<Integer>();
for(int i = 0; i < 10; i++)
{
al.add(i);
}
List<Integer> synlist = Collections.synchronizedList(al);
synchronized(synlist)
{
Iterator<Integer> itr = synlist.iterator();
while(itr.hasNext())
{
al.add(20); // It will throw ConcurrentModificationException because we cannot modify list during Iteration.
System.out.println(itr.next());
}
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Exception in thread "main" java.util.ConcurrentModificationException
在 Java 中使用 CopyOnWriteArrayList 类同步 ArrayList
1. CopyOnWriteArrayList 实现了 List 接口并创建一个空列表。
2.它按指定集合的顺序创建元素列表。
3. 是数组列表的线程安全并发访问。修改 ArrayList 时,它将创建底层数组的新副本。
4. CopyOnWriteArrayList 返回的 Iterator 和 ListIterator 是完全线程安全的。 当 ArrayList 在迭代过程中被修改时,它不会抛出 ConcurrentModificationException。
5. CopyOnWriteArrayList 不会锁定整个列表。当线程写入列表时,它只是将列表替换为底层数组的新副本。
通过这种方式,它为多个线程提供了 ArrayList 的并发访问,而不会锁定。由于读取操作是线程安全的,因此两个线程不能同时写入列表。
创建 CopyOnWriteArrayList 对象的语法如下:
语法:
代码语言:javascript代码运行次数:0运行复制CopyOnWriteArrayList<T> al = new CopyOnWriteArrayList<T>(); // T is generic.
让我们创建一个程序,我们将使用 CopyOnWriteArrayList 类同步 ArrayList 中的元素列表。
程序源代码 3:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class SynArrayListUsingCOWA
{
public static void main(String[] args)
{
// Create a thread-safe ArrayList.
CopyOnWriteArrayList<String> al = new CopyOnWriteArrayList<String>();
al.add("Pen");
al.add("Pencil");
al.add("Copy");
al.add("Eraser");
al.add("Shapner");
System.out.println("Displaying synchronized ArrayList ");
// Synchronized block is not required in this method.
Iterator<String> itr = al.iterator();
while(itr.hasNext())
{
String str = itr.next();
System.out.println(str);
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Displaying synchronized ArrayList
Pen Pencil Copy Eraser Shapner
让我们制作另一个程序,我们将尝试在迭代期间在同步列表中添加一个元素。在这里,我们将检查 CopyOnWriteArrayList 是否会在迭代期间抛出 ConcurrentModificationException。
程序源代码 4:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class AddingTest
{
public static void main(String[] args)
{
CopyOnWriteArrayList<String> al = new CopyOnWriteArrayList<String>();
al.add("A");
al.add("B");
al.add(null);
al.add("D");
al.add("E");
al.add("H");
System.out.println(al);
List<String> synlist = Collections.synchronizedList(al);
// Here, Synchronized block is not required.
// Call iterator() method using reference variable synlist.
Iterator<String> itr = synlist.iterator();
while(itr.hasNext())
{
al.set(5, "F"); // It will not throw ConcurrentModificationException during Iteration.
String str = itr.next();
System.out.println(str);
}
System.out.println(al);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[A, B, null, D, E, H]
A B null D E H
[A, B, null, D, E, F]
正如您在上面的程序中观察到的那样,在迭代过程中,我们可以使用 CopyOnWriteArrayList 方法轻松地在同步列表中添加一个元素。
ArrayList 可以在以下情况中使用
- 我们想存储重复的元素。
- 我们想要存储空元素。
- 在 Java 中,我们使用时添加和删除元素类操作比较少,需要执行大量检索性任务。
- 我们尽量不在Java的多线程环境中使用它,因为ArrayList不是线程同步的。
ArrayList全面使用
Java 中的 ArrayList 是一个可调整大小的数组,可以在内存中增长或收缩。它是使用初始容量动态创建的。
这意味着如果超过数组的初始容量,将自动创建一个容量更大的新数组,并将当前数组中的所有元素复制到新数组中。
数组列表中的元素是从零索引开始放置的。也就是说,第一个元素将放置在 0 索引处,最后一个元素将放置在索引 (n-1) 处,其中 n 是 ArrayList 的大小。
Java ArrayList 在内部使用动态数组来存储元素或数据组。
ArrayList 的容量不会自动收缩。从列表中删除元素时,数组列表的大小可以自动缩小,但不能缩小容量。
Java中ArrayList类的层次结构图
ArrayList 类的层次结构图如下图所示。
如上面的层次结构图所示,ArrayList 类实现了 List 接口并扩展了实现 List 接口的 AbstractList(抽象List)。
Java ArrayList 类还实现了 3 个标记接口:Random Access(随机访问), Cloneable(克隆), Serializable(可序列化)。
标记接口是没有任何方法或任何成员变量的接口。它也被称为空接口,因为没有字段或方法。
Random Access接口
1. Random Access接口是一个标记接口,不定义任何方法或成员。它是在 Java 1.4 版本中引入的,用于优化列表性能。
2. RandomAccess 接口存在于 java.util 包中。
3. ArrayList 类实现了一个随机访问接口,以便我们可以以相同的速度访问任何随机元素。例如,数组列表中有一亿个对象。第一个元素是 x,第 10 个元素是 y,第 1 亿个元素是 z。
现在假设第一个元素 x 可以在 1 秒内访问。由于实现了随机访问接口,第10个元素和第1亿个元素也可以在1秒内访问。
因此,我们可以以相同或恒定的速度访问任何随机元素。如果我们经常的操作是检索操作,那么 ArrayList 是最佳选择。 Serializable接口
1. 可序列化接口是用于通过网络发送对象组的标记接口。它存在于 java.io 包中。
2.它有助于将数据从一个类发送到另一个类。通常,我们使用集合来保存对象并将其从一个地方转移到另一个地方。
为了支持此要求,每个集合类都已实现可序列化和可克隆。
Cloneable接口
1. java.lang 包中存在一个Cloneable接口。
2.它用于创建完全重复的对象。当数据或对象组来自网络时,接收方将创建重复的对象。
创建完全重复的对象的过程称为克隆。这是集合类的一个非常常见的要求。
ArrayList 在 Java 中的功能
Java中ArrayList类有几个特性。它们如下:
1. 可调整大小的数组:ArrayList 是一个可调整大小的数组或可增长的数组,这意味着 ArrayList 的大小可以在运行时增加或减少大小。创建 ArrayList 后,我们可以添加任意数量的元素。
2.基于索引的结构:它在java中使用基于索引的结构。
3.重复元素:数组列表中允许重复元素。
4. 空元素:可以将任意数量的空元素添加到数组列表中。
5.插入顺序:它维护Java中的插入顺序。即插入顺序保持不变。
6. 异构对象:除TreeSet 和TreeMap外,其他都允许异构对象。异构意味着不同的元素。
7. 已同步:ArrayList未同步。这意味着多个线程可以同时使用相同的ArrayList对象。
8. 随机访问:ArrayList 实现了随机访问,因为它使用基于索引的结构。因此,我们可以从任意位置获取、设置、插入和删除数组列表的元素。
9. 性能:在 ArrayList 中,变更类的操作很慢。
例如,如果数组列表有 500 个元素,我们删除了第 50 个元素,那么第 51 个元素将尝试获取第 50 个位置,同样,所有元素也是如此。因此,它消耗了大量的时间来进行移动操作。
Java ArrayList Constructor
Java ArrayList 类提供了三个构造函数,用于创建 ArrayList 的对象。它们是:
- ArrayList()
- ArrayList(int initialCapacity)
- ArrayList(Collection c)
在Java中创建ArrayList类的对象非常简单。首先,我们将声明一个数组列表变量并调用数组列表构造函数来实例化 ArrayList 类的对象,然后将其分配给变量。
我们可以通过使用三个构造函数中的任何一个在 java 中创建 ArrayList 类的对象。让我们一起看看。
1. 创建 ArrayList 类实例的语法为:
代码语言:javascript代码运行次数:0运行复制ArrayList al = new ArrayList();
它创建一个空的 ArrayList,默认初始容量为 10。在这个数组列表中,我们只能存储 10 个元素,如下图所示。
假设我们将第 10 位的第 11 个元素插入到数组列表中,内部会发生什么?
一旦 ArrayList 达到其最大容量,ArrayList 类就会自动创建一个容量更大的新数组。
New capacity = (current capacity*3/2) + 1 = 10*3/2 + 1 = 16
创建容量更大的新数组列表后,所有现有元素都将复制到新数组列表中,然后将新元素添加到其中。将引用重新分配给新的数组对象,如上图所示。
包含对象集合的旧默认数组列表将自动进入垃圾回收。同样,如果我们尝试插入第 17 个元素,新容量 = 16*3/2 + 1 = 25。
2. 我们还可以在创建 ArrayList 对象时初始化容量。创建具有初始容量的 ArrayList 类实例的语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList al = new ArrayList(int initialCapacity);
它创建一个具有初始容量的空数组列表。如果知道初始容量,可以直接使用此方法;否则我们就应该采用默认值以提高系统的性能。
例如,假设我们的要求是在数组列表中添加 500 个元素,我们将创建如下的 ArrayList 对象:
代码语言:javascript代码运行次数:0运行复制ArrayList list2 = new ArrayList(500);
3. 我们还可以通过集合参数的方式来初始化ArrayList 类。语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList al = new ArrayList(Collection c);
它通过一个给定的集合 c 的元素来创建 ArrayList 对象。
例如:
代码语言:javascript代码运行次数:0运行复制ArrayList list3 = new ArrayList(list1); // list1 is elements of collection.
在 Java 中创建泛型数组列表对象
Java 1.5 或更高版本还提供了我们可以指定 ArrayList 对象中元素的类型方法。例如,我们可以创建一个的字符串数组泛型列表对象,如下所示:
代码语言:javascript代码运行次数:0运行复制 ArrayList<String> al = new ArrayList<String>(); // It can be used to store only String type.
// The advantage of specifying a type is that when we try to add another type of element, it will give compile-time error.
or,
Creating a Generic ArrayList object can also be done in separate lines like this:
ArrayList<String> arlist;
arlist = new ArrayList();
注意:我们不能使用原始数据类型作为类型。例如,ArrayList<int>是非法的。
Java ArrayList Initialization
在 Java 中初始化数组列表有三种方法。它们如下:
1. 使用 Arrays.asList:使用 asList() 方法初始化 ArrayList 的语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList<Type> list = new ArrayList<Type>(Arrays.asList(Object o1, Object o2, .. so on));
For example:
ArrayList<String> ar = new ArrayList<String>(Arrays.asList("A", "B", "C"))
2:使用普通方式:这是在java程序中初始化ArrayList的流行方法。初始化数组列表的语法如下:
代码语言:javascript代码运行次数:0运行复制ArrayList<Type> obj = new ArrayList<Type>();
obj.add("Obj o1");
obj.add("Obj o2");
and so on.
3. 使用匿名内部类:在java中使用匿名内部类初始化ArrayList的语法为:
代码语言:javascript代码运行次数:0运行复制ArrayList<Type> arl = new Arraylist<Type>() {{
add(Object o1);
add(Object o2);
add(Object o3);
. . . . . . . .
. . . . . . .
}};
Java 中的 ArrayList Methods
ArrayList还提供了一些有用的方法。它们如下:
1. boolean add(Object o):此方法用于在数组列表末尾添加一个元素。例如,如果你想在列表的末尾添加一个元素,你只需像这样调用 add() 方法:
代码语言:javascript代码运行次数:0运行复制list.add("Shubh"); // This will add "Shubh" at the end of the list.
2. boolean addAll(Collection c): 此方法用于在列表末尾添加特定集合中的一组元素。例如,假设我们在 list2 中有一组元素,并希望在 list1 的末尾添加,我们将像这样调用此方法:
代码语言:javascript代码运行次数:0运行复制 list1.addAll(list2);
3. boolean addAll(int index, Collection c):此方法用于在列表中的指定位置添加一组元素。例如:
代码语言:javascript代码运行次数:0运行复制list1.addAll(2, list2): // Adding all elements of list2 at position index 2 in list1
4. void add(int index, Object o):用于在列表中的特定位置索引处添加一个元素。例如:
代码语言:javascript代码运行次数:0运行复制list.add(3, "a"); // Adding element 'a' at position index 3 in list.
5. void addAll(int index, Object o):用于在列表中的特定位置添加特定元素。例如,假设我们想在列表中的位置 2 处放置一个特定的元素 “Shubh”,我们将调用 add(int index, Object o) 方法,如下所示:
代码语言:javascript代码运行次数:0运行复制list.add(2,"Shubh"); // This will add "Shubh" at the second position.
让我们以一个基于上述 ArrayList add() 方法的示例程序为例。
程序源代码 1:
代码语言:javascript代码运行次数:0运行复制package ArrayListTest;
import java.util.ArrayList;
public class AddExample
{
public static void main(String[] args)
{
// Create an object of the non-generic ArrayList.
ArrayList al = new ArrayList(); // list 1 with default capacity 10.
al.add("A");
al.add("B");
al.add(20);
al.add("A");
al.add(null);
System.out.println(al);
// Create an object of another non-generic ArrayList.
ArrayList al1 = new ArrayList(); // List 2.
al1.add("a");
al1.add("b");
al1.add("c");
// Call addAll(Collection c) method using reference variable al to add all elements at the end of the list1.
al.addAll(al1);
System.out.println(al);
// Call addAll(int index, Collection c) method using reference variable al1 to add all elements at specified position 2.
al1.addAll(2, al);
System.out.println(al1);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[A, B, 20, A, null]
[A, B, 20, A, null, a, b, c]
[a, b, A, B, 20, A, null, a, b, c, c]
在前面的示例程序中,我们使用了重复元素、异构元素(即字符串和整数)和空元素。您还会注意到,当在特定位置添加指定元素时,右侧元素会向右移动一个位置。
移位后,对象引用变量将重新分配一个新的数组列表,如上图所示。由于转移,ArrayList也很耗时。
在输出中,我们得到了这个数组列表的方括号表示法。这是因为当我们尝试打印对象引用变量时,它会在内部调用 toString() 方法,如下所示:
代码语言:javascript代码运行次数:0运行复制System.out.println(al); ------> System.out.println(al.toString());
5. void addAll(int index, Object o): 如果指定元素存在,它将从此列表中删除该元素的第一个匹配项。
6. void remove(int index):此方法从列表中的特定位置删除元素。请看下面的例子。
代码语言:javascript代码运行次数:0运行复制list.remove("A");
list.remove(2); // It will remove element from position 2.
7. void clear():clear() 方法用于从数组列表中删除所有元素。
8. void set(int index, Object o):set() 方法用指定的元素替换列表中特定位置的元素。例如,假设我们要将位置 2 的元素 “A” 替换为列表中的元素 “a”,我们将不得不将此方法调用为:
代码语言:javascript代码运行次数:0运行复制list.set(2, "a");
让我们举一个示例程序,我们将使用 remove() 方法从列表中删除一个元素。
程序源代码 2:
代码语言:javascript代码运行次数:0运行复制package ArrayListTest;
import java.util.ArrayList;
public class RemoveEx
{
public static void main(String[] args)
{
// Create a generic Arraylist object of String type.
// This means the compiler will show an error if we try to put any other element than String.
ArrayList<String> al = new ArrayList<String>(); // Default capacity is 10.
// Adding elements of String type.
al.add("A");
al.add("B");
al.add("C");
al.add("D");
al.add(null);
al.add("D");
System.out.println(al);
// Call remove() method to remove element D.
al.remove("D"); // removes the first occurrence of the specified element D at position 3, not from the position 5.
System.out.println(al);
al.remove(3);
System.out.println(al);
// Call set method to replace the element D with a null element at position 3.
al.set(3, null);
System.out.println(al);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[A, B, C, D, null, D]
[A, B, C, null, D]
[A, B, C, D]
[A, B, C, null]
在此示例程序中,您将观察到,当从数组中删除或删除元素时,已删除的元素变为 null,但已删除元素占用的空插槽仍保留在数组中。
数组右侧的任何后续元素都会自动移动到左侧的一个位置,以填充被已删除元素占用的空插槽,对象引用变量 'al' 将被重新分配给新的数组列表,如上图所示。
9. Object get(int index)::它返回此列表中指定位置的元素。
10. int size():它返回列表的元素数。大小 表示数组列表中存在的元素数。容量是指存储元素的能力。
11. Object get(int index):如果列表中存在指定的元素,则返回 true,否则返回 false。请看下面的例子。
代码语言:javascript代码运行次数:0运行复制String str = list.get(2);
int numberOfElements = list.size();
list.contains("A");
让我们看一个基于这些方法的示例程序。
程序源代码 3:
代码语言:javascript代码运行次数:0运行复制package arrayListPrograms;
import java.util.ArrayList;
public class ArrayListTest
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add("Apple");
al.add("Orange");
al.add("Banana");
al.add("Gauva");
System.out.println(al);
// Call get() method using object reference variable 'al' to get the specified element.
// Since return type of get() method is String. Therefore, we will store it by using a fruitsName variable with data type String.
String fruitsName = al.get(2);
System.out.println(fruitsName);
// Call size() method to get the number of elements present in the list.
// Since return type of size method is an integer. Therefore, we will store it by using variable numberOfElements with data type integer.
int numberOfElements = al.size();
System.out.println(numberOfElements);
// Check apple element is present or not.
boolean check = al.contains("Apple");
System.out.println(check);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[Apple, Orange, Banana, Gauva]
Banana
4
true
12. int indexOf(Object o):用于获取指定元素第一次出现的索引,如果列表中不存在该元素,则返回值 -1。
13. int lastIndexOf(Object o):用于获取列表中指定元素最后一次出现的索引。如果列表中不存在该元素,则返回 -1。例如:
代码语言:javascript代码运行次数:0运行复制int pos = list.indexOf("Apple");
int lastPos = list.lastIndexOf(20);
程序源代码 4:
代码语言:javascript代码运行次数:0运行复制package ArrayListTest;
import java.util.ArrayList;
public class Test
{
public static void main(String[] args)
{
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
System.out.println(list);
int pos = list.indexOf(30);
System.out.println(pos);
int lastPos = list.lastIndexOf(40);
System.out.println(lastPos);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[10, 20, 30, 40]
23
我们如何手动增加或减少 ArrayList 的当前容量?
1. ensureCapacity():此方法用于增加 ArrayList 的当前容量。由于当我们添加更多元素时,数组列表的容量会自动增加。但是要手动增加,请使用 ArrayList 类的 ensureCapacity() 方法。 2. trimTosize():trimTosize() 方法用于将 ArrayList 的容量修剪为 ArrayList 的当前大小。
代码语言:javascript代码运行次数:0运行复制ArrayList<String> list = new ArrayList<String>(); // Here, list can hold 10 elements.(Default initial capacity).
list.ensureCapacity(20); // Now it can hold 20 elements.
list.trimTosize();
如何在 Java 中同步 ArrayList
什么是 Java 中的同步?
从技术上讲,java 中的同步是一次只允许一个线程完全完成任务的过程。它只允许一个线程访问单个资源或单个任务。
当多个线程同时访问相同的资源时,程序可能会产生不是期望值的输出。因此,可以使用称为同步的技术来解决此问题。同步的目的是控制对共享资源的访问。
我们知道默认情况下 ArrayList 类不是线程安全。这意味着多个线程可以同时访问同一个 ArrayList 对象或实例。
因此,如果没有显式同步,它不能在多线程环境中被正确地使用。这是因为如果在多线程环境中使用 ArrayList 而不使用同步技术,那么它可能会产生不可预测的输出。
程序源代码 5:
代码语言:javascript代码运行次数:0运行复制package arrayListProgram;
import java.util.ArrayList;
public class MultiThreadEx extends Thread
{
// Declare ArrayList variable.
ArrayList<Integer> list;
// Create a one parameter constructor with parameter 'list' and type ArrayList.
MultiThreadEx(ArrayList<Integer> list)
{
this.list = list;
}
@Override
public void run()
{
System.out.println("Run method");
for(int i = 0 ; i < = 6; i++)
{
// Adding elements in the list.
list.add(i);
try {
// Call sleep() method to delay some time.
Thread.sleep(50);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
// Creates an ArrayList object with an initial capacity of 10.
ArrayList<Ineger> list = new ArrayList<Integer>();
// Create multiple thread class objects m1, m2 and passes 'list' as a parameter.
MultiThreadEx m1 = new MultiThreadEx(list);
MultiThreadEx m2 = new MultiThreadEx(list);
// Call start() method to start thread using m1 and m2 reference variable.
m1.start();
m2.start();
try {
// Call join() method to complete the task of adding elements in the ArrayList.
m1.join();
m2.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Integer sizelist = list.size();
System.out.println("Size of list is " + sizelist);
for(Integer i : list)
{
System.out.println("num - " + i);
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Run method
Run method
Size of list is 12
num - 0
num - 0
num - 1
num - 2
num - 2
num - 3
num - 3
num - 4
num - 4
num - 5
num - 5
num - 6
解释:
正如您在前面的示例程序中观察到的那样,ArrayList 实例在类对象 m1 和 m2 的两个线程之间共享。每个线程都尝试在 ArrayList 中添加 6 个元素。由于数组列表未同步。
因此,预期的输出是不可预测的。当我们运行上面的代码时,它显示 ArrayList 的大小是 12(而不是 14)。元素 1 和 6 在列表中仅插入一次。
让我们看看上面的程序发生了什么。
➤ 从主线程创建线程 1 时,它会在列表中插入元素 0。在列表中添加元素 0 后,线程 1 进入睡眠位置。
由于我们使用join()方法完成通过m1将元素从0添加到6的任务,所以当thread-1进入睡眠状态时,同时,主线程将启动thread-2执行,并将在列表中添加一个元素0。
➤ 当线程 1 的休眠时间结束时,线程 1 将再次进入运行状态,并在列表中添加元素 1。添加后,它将再次进入睡眠位置。
➤ 休眠时间结束后,它再次进入运行状态以插入元素 2。添加后,线程 1 将再次进入睡眠状态。同时,主线程将再次启动线程 2 以完成其任务并添加不可预测的元素 2。
正如您在程序中观察到的那样,在多线程环境中使用 ArrayList 并不能提供线程安全性。这是因为 ArrayList 未同步。
那么,我们将如何在Java中获得具有线程安全性的同步ArrayList呢?
有两种方法可以获取 ArrayList 的同步版本。它们如下:
- 通过使用 Collections.syncdList() 方法
- 通过使用 CopyOnWriteArrayList
使用 Collections.syncdList() 方法同步 ArrayList
此方法用于同步 Java 中的集合。此方法的语法如下:
语法:
代码语言:javascript代码运行次数:0运行复制public static List<T> synchronizedList(List<T> list)
1. syncdList() 方法接受 List,它可以是 List 接口的实现。例如,ArrayList 和 LinkedList。 2. 此方法的返回类型是同步列表(线程安全)。
3. 同步列表是方法的名称。
4. 参数列表是要包装在同步列表中的列表。
5. T 表示泛型的类型。
注意:迭代器在同步块中用于同步,以避免 Java 中的 ConcurrentModificationException。同步的 ArrayList 返回的迭代器是快速故障的。当在迭代期间列表中发生任何修改时,它将抛出 ConcurrentModificationException。
让我们举一个基本的例子来同步 java 中的 ArrayList。
代码语言:javascript代码运行次数:0运行复制ArrayList<String> al = new ArrayList<String>(); // Non-synchronized.
List l = Collections.synchronizedList( al );
l ➟ synchronized.
al ➟ Non-synchronized.
or,
List<String> synlist = Collections.synchronizedList( new ArrayList<String>);
同样,我们可以使用以下语法获取 Set, Map 对象的同步版本:
代码语言:javascript代码运行次数:0运行复制public static Set synchronizedList( Set s );
public static Map synchronizedList( Map m );
让我们举一个示例程序,其中我们将同步未同步的 ArrayList 对象列表,然后我们将调用 iterator() 方法来迭代同步的 ArrayList 对象列表。
程序源代码 1:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class ArrayListSynchronizationTest
{
public static void main(String[] args)
{
// Create an ArrayList object with initial capacity of 10.
// Non-synchronized ArrayList Object.
List<String> l = new ArrayList<String>();
// Add elements in the list.
l.add("Apple");
l.add("Orange");
l.add("Banana");
l.add("Pineapple");
l.add("Guava");
// Synchronizing ArrayList in Java.
List<String> synlist = Collections.synchronizedList( l ); // l is non-synchronized.
// Here, we will use a synchronized block to avoid the non-deterministic behavior.
synchronized(synlist)
{
// Call iterator() method to iterate the ArrayList.
Iterator<String> itr = synlist.iterator();
while(itr.hasNext())
{
String str = itr.next();
System.out.println(str);
}
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Apple Orange Banana Pineapple Guava
在前面的程序中,ArrayList 通过调用将整个列表锁定到同步块中的 syncdList() 方法来实现线程安全。 让我们以另一个程序为例,我们将通过在迭代期间向同步列表中添加一个数字来检查 ConcurrentModificationException 的出现。
程序源代码 2:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class CMETest
{
public static void main(String[] args)
{
ArrayList<Integer> al = new ArrayList<Integer>();
for(int i = 0; i < 10; i++)
{
al.add(i);
}
List<Integer> synlist = Collections.synchronizedList(al);
synchronized(synlist)
{
Iterator<Integer> itr = synlist.iterator();
while(itr.hasNext())
{
al.add(20); // It will throw ConcurrentModificationException because we cannot modify list during Iteration.
System.out.println(itr.next());
}
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Exception in thread "main" java.util.ConcurrentModificationException
在 Java 中使用 CopyOnWriteArrayList 类同步 ArrayList
1. CopyOnWriteArrayList 实现了 List 接口并创建一个空列表。
2.它按指定集合的顺序创建元素列表。
3. 是数组列表的线程安全并发访问。修改 ArrayList 时,它将创建底层数组的新副本。
4. CopyOnWriteArrayList 返回的 Iterator 和 ListIterator 是完全线程安全的。 当 ArrayList 在迭代过程中被修改时,它不会抛出 ConcurrentModificationException。
5. CopyOnWriteArrayList 不会锁定整个列表。当线程写入列表时,它只是将列表替换为底层数组的新副本。
通过这种方式,它为多个线程提供了 ArrayList 的并发访问,而不会锁定。由于读取操作是线程安全的,因此两个线程不能同时写入列表。
创建 CopyOnWriteArrayList 对象的语法如下:
语法:
代码语言:javascript代码运行次数:0运行复制CopyOnWriteArrayList<T> al = new CopyOnWriteArrayList<T>(); // T is generic.
让我们创建一个程序,我们将使用 CopyOnWriteArrayList 类同步 ArrayList 中的元素列表。
程序源代码 3:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class SynArrayListUsingCOWA
{
public static void main(String[] args)
{
// Create a thread-safe ArrayList.
CopyOnWriteArrayList<String> al = new CopyOnWriteArrayList<String>();
al.add("Pen");
al.add("Pencil");
al.add("Copy");
al.add("Eraser");
al.add("Shapner");
System.out.println("Displaying synchronized ArrayList ");
// Synchronized block is not required in this method.
Iterator<String> itr = al.iterator();
while(itr.hasNext())
{
String str = itr.next();
System.out.println(str);
}
}
}
代码语言:javascript代码运行次数:0运行复制Output:
Displaying synchronized ArrayList
Pen Pencil Copy Eraser Shapner
让我们制作另一个程序,我们将尝试在迭代期间在同步列表中添加一个元素。在这里,我们将检查 CopyOnWriteArrayList 是否会在迭代期间抛出 ConcurrentModificationException。
程序源代码 4:
代码语言:javascript代码运行次数:0运行复制package synchronizeTest;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class AddingTest
{
public static void main(String[] args)
{
CopyOnWriteArrayList<String> al = new CopyOnWriteArrayList<String>();
al.add("A");
al.add("B");
al.add(null);
al.add("D");
al.add("E");
al.add("H");
System.out.println(al);
List<String> synlist = Collections.synchronizedList(al);
// Here, Synchronized block is not required.
// Call iterator() method using reference variable synlist.
Iterator<String> itr = synlist.iterator();
while(itr.hasNext())
{
al.set(5, "F"); // It will not throw ConcurrentModificationException during Iteration.
String str = itr.next();
System.out.println(str);
}
System.out.println(al);
}
}
代码语言:javascript代码运行次数:0运行复制Output:
[A, B, null, D, E, H]
A B null D E H
[A, B, null, D, E, F]
正如您在上面的程序中观察到的那样,在迭代过程中,我们可以使用 CopyOnWriteArrayList 方法轻松地在同步列表中添加一个元素。
ArrayList 可以在以下情况中使用
- 我们想存储重复的元素。
- 我们想要存储空元素。
- 在 Java 中,我们使用时添加和删除元素类操作比较少,需要执行大量检索性任务。
- 我们尽量不在Java的多线程环境中使用它,因为ArrayList不是线程同步的。
本文标签: ArrayList全面使用
版权声明:本文标题:ArrayList全面使用 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1747966902a2234930.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论