Java ArrayList

时间:2020-02-23 14:36:22  来源:igfitidea点击:

Java ArrayList是使用最广泛的Collection类之一。
java.util.ArrayList类实现了java.util.List接口。
Java ArrayList还实现RandomAccess,Cloneable和Serializable接口。
Java ArrayList类扩展了AbstractList类,该类是List接口的基本实现。

Java ArrayList

Java ArrayList是List接口的可调整大小的数组实现,这意味着它以默认大小开始,并在向数组列表中添加更多数据时自动增长。
关于Java ArrayList的一些重要点是:

  • Java ArrayList除了不同步外,几乎与Vector相似,因此在单线程环境中性能更好。

  • Java ArrayList不是线程安全的,因此在多线程环境中使用时必须格外小心。

  • Java ArrayList可以包含重复值,它也允许"空"值。

  • java ArrayList中的对象按顺序添加。
    因此,您始终可以按索引0检索第一个对象。

  • Java ArrayList的默认容量定义为10。
    但是,我们可以通过其构造函数或者调用ensureCapacity(int minCapacity)方法来更改默认容量。

  • Java ArrayList Iterator和ListIterator实现是快速失败的。
    如果在创建迭代器之后以除迭代器add或者remove方法之外的任何其他方式修改了列表结构,则它将抛出ConcurrentModificationException。

  • Java ArrayList提供了对其元素的随机访问,因为它可以在索引上使用。
    我们可以通过索引来检索任何元素。

  • Java ArrayList支持泛型,这是创建ArrayList的推荐方法。

Java ArrayList构造函数

Java ArrayList类中有三个构造函数。

  • public ArrayList():最广泛使用的Java ArrayList构造函数。
    此ArrayList构造函数将返回一个初始容量为10的空列表。

  • public ArrayList(int initialCapacity):此ArrayList构造函数将返回一个空列表,其初始容量由initialCapacity参数指定。
    当您知道列表将包含大量数据并且希望通过提供较大的初始容量值来节省重新分配的时间时,此构造函数很有用。
    如果initialCapacity参数为负,它将抛出IllegalArgumentException

  • public ArrayList(Collection <?extends E> c):此ArrayList构造函数将返回一个列表,该列表包含指定集合的元素,此列表按集合的迭代器返回的顺序排列。
    如果指定的collection参数为null,它将抛出著名的NullPointerException

以下是显示正在使用的Java ArrayList构造函数的简单代码段。

List list = new ArrayList(); //not recommended
List<String> list1 = new ArrayList<String>(); //recommended way

Java ArrayList方法

Java ArrayList包含许多我们经常使用的方法。

  • public boolean add(E e):将指定的元素追加到此列表的末尾。

  • public void add(int index,E element):将指定的元素插入列表中的指定位置。
    将当前在该位置的元素(如果有)和任何后续元素右移。
    如果index大于列表大小或者为负,它将抛出IndexOutOfBoundsException。

  • public boolean addAll(Collection <?extends E> c):将指定集合中的所有元素按指定集合的Iterator返回的顺序追加到此列表的末尾。
    如果指定的集合为null,则此操作将引发NullPointerException。

  • public boolean addAll(int index,Collection <?extended E> c):从指定位置开始,将指定集合中的所有元素插入此列表。
    将当前在该位置的元素(如果有)和任何后续元素右移(增加其索引)。
    如果索引值大于列表大小或者为负数,则此方法将引发IndexOutOfBoundsException。
    如果指定的collection为null,则此方法还会引发NullPointerException。

  • public boolean contains(Object o):如果此列表包含指定的元素,则返回true。

  • public void clear():从此列表中删除所有元素。

  • public void sureCapacity(int minCapacity):如有必要,增加此ArrayList实例的容量,以确保它至少可以容纳最小容量参数指定的元素数。

  • public void forEach(Consumer <?super E> action):对Iterable的每个元素执行给定的操作,直到处理完所有元素或者该操作引发异常为止。

  • public E get(int index):返回此列表中指定位置的元素。

  • public boolean isEmpty():如果此列表不包含任何元素,则返回true。

  • public int indexOf(Object o):返回此列表中指定元素的首次出现的索引;如果此列表不包含该元素,则返回-1。

  • public Iterator <E> iterator():以适当的顺序返回此列表中元素的迭代器。
    返回的迭代器是快速失败的。

  • public int lastIndexOf(Object o):返回指定元素在此列表中最后一次出现的索引;如果此列表不包含该元素,则返回-1。

  • public ListIterator <E> listIterator():返回此列表中元素的列表迭代器(按适当顺序)。
    返回的列表迭代器是快速失败的。

  • public ListIterator <E> listIterator(int index):从列表中的指定位置开始(按正确顺序)返回此列表中元素的列表迭代器。
    指定的索引指示首次调用next将返回的第一个元素。
    最初调用previous将返回指定索引减一的元素。
    如果索引值大于列表大小或者为负数,则此方法将引发IndexOutOfBoundsException。

  • public E remove(int index):删除此列表中指定位置的元素。
    将所有后续元素向左移动(从其索引中减去一个)。

  • public boolean remove(Object o):从此列表中删除第一次出现的指定元素(如果存在)。
    如果列表不包含该元素,则该元素不变。

  • public boolean removeAll(Collection <E> c):从此列表中删除所有包含在指定集合中的元素。

  • public boolean keepAll(Collection <E> c):仅保留此列表中包含在指定集合中的元素。
    换句话说,从该列表中删除所有未包含在指定集合中的元素。

  • public boolean removeIf(Predicate <?super E> filter):移除此集合中满足给定谓词的所有元素。

  • public void replaceAll(UnaryOperator <E> operator):使用将该运算符应用于该元素的结果替换此列表中的每个元素。

  • public int size():返回此列表中的元素数。

  • public E set(int index,E element):用指定元素替换此列表中指定位置的元素。

  • public List <E> subList(int fromIndex,int toIndex):返回列表的一部分在指定的fromIndex(包括)和toIndex(不包括)之间的视图。
    返回列表由该列表支持,因此返回列表中的非结构性更改会反映在此列表中,反之亦然。

  • public Spliterator <E> splitter():在此列表中的元素上创建后绑定和故障快速的Spliterator。

  • public void sort(Comparator <?super E> c):根据由指定Comparator引起的顺序对该列表进行排序。

  • public void trimToSize():将此ArrayList实例的容量调整为列表的当前大小。
    应用程序可以使用此操作来最小化ArrayList实例的存储。

  • public Object [] toArray():返回一个包含此列表中所有元素的数组,该数组以正确的顺序排列(从第一个元素到最后一个元素)。
    返回的数组将是"安全的",因为此列表不保留对其的引用。
    (换句话说,此方法必须分配一个新数组)。
    因此,调用者可以自由修改返回的数组。

  • public <T> T [] toArray(T [] a):以正确的顺序返回包含此列表中所有元素的数组。
    如果列表适合指定的数组,则按原样返回。
    否则,将使用指定数组的运行时类型和此列表的大小分配一个新数组。

Java ArrayList示例

让我们通过一些程序看一下ArrayList方法示例。

Java ArrayList的常用操作

下面是一个简单的Arraylist示例程序,显示了常用方法。

//Java ArrayList default constructor
List<String> vowels = new ArrayList<String>();

//Java ArrayList constructor with initial capacity
List<String> dictionaryWordsList = new ArrayList<String>(50000);

vowels.add("A");
vowels.add("B");
vowels.add("C");
vowels.add("D");
vowels.add("E");

//Creating my list from different collection source
List<String> myList = new ArrayList<String>(vowels);

上面程序的输出如下。

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Java ArrayList Example Program
 * 
 * @author hyman
 *
 */
public class ArrayListExample {

	public static void main(String args[]) {
		List<String> letters = new ArrayList<String>();
		
		//add example
		letters.add("A");
		letters.add("C");
		letters.add("D");
		
		//let's insert B between A and C
		letters.add(1,"B");
		System.out.println(letters);
		
		List<String> list = new ArrayList<String>();
		list.add("E");list.add("H");
		
		//appending list elements to letters
		letters.addAll(list);
		System.out.println(letters);
		
		//clear example to empty the list
		list.clear();
		
		list.add("F");list.add("G");
		
		//inserting list inside letters to get right sequence
		letters.addAll(5, list);
		System.out.println(letters);
		
		//contains example
		System.out.println("Letters list contains E ? "+letters.contains("E"));
		System.out.println("Letters list contains Z ? "+letters.contains("Z"));
		
		//ensureCapacity example, it's ArrayList method, so object should be defined like below.
		ArrayList<String> tempList = new ArrayList<>();
		tempList.ensureCapacity(1000);
		
		//get example
		String e = letters.get(4);
		System.out.println("Letter at 5th place: "+e);
		
		//tempList is empty?
		System.out.println("tempList is empty ? "+tempList.isEmpty());
		
		//indexOf example
		System.out.println("First index of D = "+letters.indexOf("D"));
		System.out.println("Last index of D = "+letters.lastIndexOf("D"));
		
		//remove examples
		System.out.println(letters);
		String removed = letters.remove(3);
		System.out.println("After removing '"+removed+"' letters contains "+letters);
		
		//remove first occurrence of H
		boolean isRemoved = letters.remove("H");
		System.out.println("H removed? "+isRemoved+". Letters contains "+letters);
		System.out.println("list contains "+list);
		
		//remove all matching elements between letters and list
		letters.removeAll(list);
		System.out.println(letters);
		
		//retainAll example
		list.clear();list.add("A");list.add("B");list.add("C");
		letters.retainAll(list);
		System.out.println("letters elements after retainAll operation: "+letters);
		
		//size example
		System.out.println("letters ArrayList size = "+letters.size());
		
		//set example
		letters.set(2, "D");
		System.out.println(letters);
		
		//toArray example
		String[] strArray = new String[letters.size()];
		strArray = letters.toArray(strArray);
		System.out.println(Arrays.toString(strArray));
	}
}

Java ArrayList forEach

Java 8中添加了Java ArrayList forEach方法。
当您要对所有元素执行相同的操作时,此方法很有用。
方法参数Consumer是一个功能接口,因此我们也可以使用lambda表达式。
下面是forEach方法的示例,该方法显示了旧式方法以及lambda表达方法。

[A, B, C, D]
[A, B, C, D, E, H]
[A, B, C, D, E, F, G, H]
Letters list contains E ? true
Letters list contains Z ? false
Letter at 5th place: E
tempList is empty ? true
First index of D = 3
Last index of D = 3
[A, B, C, D, E, F, G, H]
After removing 'D' letters contains [A, B, C, E, F, G, H]
H removed? true. Letters contains [A, B, C, E, F, G]
list contains [F, G]
[A, B, C, E]
letters elements after retainAll operation: [A, B, C]
letters ArrayList size = 3
[A, B, D]
[A, B, D]

ArrayList forEach示例程序产生的输出为:

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class ArrayListForEachExample {

	public static void main(String[] args) {
		
		List<String> stocks = new ArrayList<>();
		stocks.add("Google"); stocks.add("Apple");
		stocks.add("Microsoft"); stocks.add("Facebook");
		
		Consumer<Object> consumer = new ArrayListForEachExample().new MyConsumer();
		
		stocks.forEach(consumer);
		
		//lambda style
		stocks.forEach(x -> {System.out.println("Processed "+x);});
		
	}

	class MyConsumer implements Consumer<Object>{

		@Override
		public void accept(Object t) {
			System.out.println("Processing "+t);
		}
		
	}
}

Java ArrayList迭代器

Iterator是Java Collections框架中的接口。
ArrayList提供快速失败的迭代器实现。
当您要对所有列表元素执行某些操作时,应使用Iterator。
如果在迭代过程中对列表进行了结构上的修改,则next()操作将抛出ConcurrentModificationException。
以下是ArrayList迭代器的简单示例。

Processing Google
Processing Apple
Processing Microsoft
Processing Facebook
Processed Google
Processed Apple
Processed Microsoft
Processed Facebook

上面的ArrayList迭代器示例程序产生的输出是:

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ArrayListIteratorExample {

	public static void main(String[] args) {

		List<Integer> ints = new ArrayList<>();
		for(int i=0; i<10; i++) ints.add(i);
		
		Iterator<Integer> it = ints.iterator();
		
		//simple iteration
		while(it.hasNext()){
			int x = (int) it.next();
			System.out.print(x + ", ");
		}
		System.out.println("\n"+ints);
		
		//modification of list through iterator
		it = ints.iterator();
		while(it.hasNext()){
			int x = (int) it.next();
			if(x%2 ==0) it.remove();
		}
		System.out.println(ints);
		
		//changing list structure while iterating
		it = ints.iterator();
		while(it.hasNext()){
			int x = (int) it.next(); //ConcurrentModificationException here
			if(x==5) ints.add(20);
		}
	}

}

阅读有关ConcurrentModificationException的更多信息以及如何避免它。

Java ArrayList ListIterator

我们可以使用ListIterator在两个方向上遍历列表。
它允许我们删除元素以及将元素添加到列表中。
您还可以在ListIterator中获取迭代器的当前位置。
让我们看一个简单的ArrayList ListIterator示例,该示例用于向后遍历列表并修改列表数据。

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 3, 5, 7, 9]
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at com.theitroad.examples.ArrayListIteratorExample.main(ArrayListIteratorExample.java:34)

注意上面程序产生的输出。

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ArrayListListIteratorExample {

	public static void main(String[] args) {

		List<Integer> ints = new ArrayList<>();
		for (int i = 0; i < 10; i++) ints.add(i);
		
		ListIterator<Integer> lit = ints.listIterator(ints.size());
		
		while(lit.hasPrevious()){
			int x = lit.previous();
			System.out.print(x + ", ");
			if(x==5){
				lit.remove();
				lit.add(20);
			}
		}
		System.out.println("\n"+ints);
	}
}

Java ArrayList removeIf

在Java 8中添加了ArrayList removeIf方法。
此方法将删除列表中满足给定谓词的所有元素。
让我们看一下Java ArrayList removeIf示例的简单程序。

9, 8, 7, 6, 5, 20, 4, 3, 2, 1, 0, 
[0, 1, 2, 3, 4, 20, 6, 7, 8, 9]

下面是上面程序的输出。

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class ArrayListRemoveIfExample {

	public static void main(String[] args) {
		List<Integer> ints = new ArrayList<>();
		for (int i = 0; i < 10; i++) ints.add(i);
		
		Predicate<Integer> filter = new ArrayListRemoveIfExample(). new MyPredicate();
		
		ints.removeIf(filter);
		
		System.out.println(ints);
		
		//lambda expression, remove elements divisible by 3
		ints.removeIf(x -> {return x %3 == 0;});
		
		System.out.println(ints);
	}

	class MyPredicate implements Predicate<Integer> {

		@Override
		public boolean test(Integer t) {
			return t %2 == 0;
		}
		
	}
}

Java ArrayList replaceAll

ArrayList replaceAll方法是Java 8中添加的。
当您希望对列表的所有元素应用某些功能时,此方法很有用。
让我们看一下ArrayList replaceAll示例程序。

[1, 3, 5, 7, 9]
[1, 5, 7]

以下是上述replaceAll示例程序的输出。

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.List;
import java.util.function.UnaryOperator;

public class ArrayListReplaceAllExample {

	public static void main(String[] args) {
		List<Integer> ints = new ArrayList<>();
		for (int i = 0; i < 10; i++) ints.add(i);
		
		//multiply all elements by 10
		UnaryOperator<Integer> operator = new ArrayListReplaceAllExample(). new MyUnaryOperator();
		ints.replaceAll(operator);
		System.out.println(ints);
		
		//lambda expression example, multiply by 5
		ints.replaceAll(x -> {return x*5;});
		System.out.println(ints);
	}

	class MyUnaryOperator implements UnaryOperator<Integer>{

		@Override
		public Integer apply(Integer t) {
			return t*10;
		}
		
	}
}

Java ArrayList子列表

当我们对列表使用subList方法时,它返回原始列表一部分的视图。
此新列表由原始列表支持,因此任何修改也将反映到其他列表。
如果以非通过返回列表的方式对后备列表进行结构上的修改,则此方法返回的列表的语义将变得不确定。
新列表上的所有方法首先检查后备列表的实际modCount是否等于其期望值,如果不相等,则抛出ConcurrentModificationException。
让我们通过一个简单的ArrayList subList示例来了解这种行为。

[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
[0, 50, 100, 150, 200, 250, 300, 350, 400, 450]

下面是上面的ArrayList subList示例程序的输出。

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.List;

public class ArrayListSubListExample {

	public static void main(String[] args) {
		
		List<String> names = new ArrayList<>();
		names.add("hyman"); names.add("David");names.add("Lisa");names.add("Meghna");
		
		List<String> first2Names = names.subList(0, 2);
		
		System.out.println(names +" , "+first2Names);
		
		names.set(1, "Kumar");
		//check the output below. :)
		System.out.println(names +" , "+first2Names);
		
		first2Names.add("Megan"); //this is fine
		System.out.println(names +" , "+first2Names); //this is fine
		
		//Let's modify the list size and get ConcurrentModificationException
		names.add("hyman");
		System.out.println(names +" , "+first2Names); //this line throws exception

	}

}

Java ArrayList排序

我们可以使用ArrayList排序方法对其元素进行排序。
以下是显示ArrayList排序的简单示例。

[hyman, David, Lisa, Meghna] , [hyman, David]
[hyman, Kumar, Lisa, Meghna] , [hyman, Kumar]
[hyman, Kumar, Megan, Lisa, Meghna] , [hyman, Kumar, Megan]
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)
	at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091)
	at java.util.AbstractList.listIterator(AbstractList.java:299)
	at java.util.ArrayList$SubList.iterator(ArrayList.java:1087)
	at java.util.AbstractCollection.toString(AbstractCollection.java:454)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.theitroad.examples.ArrayListSubListExample.main(ArrayListSubListExample.java:26)

上述排序程序的输出为:

package com.theitroad.examples;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

public class ArrayListSortingExample {

	public static void main(String[] args) {
		List<Integer> ints = new ArrayList<>();
		Random random = new Random();
		for (int i = 0; i < 10; i++) ints.add(random.nextInt(1000));
		
		System.out.println("Original List: "+ints);
		
		//sort the list
		MyComparator c = new ArrayListSortingExample(). new MyComparator();
		ints.sort(c);
		System.out.println("Sorted in Increasing Order: "+ints);
		
		//lambda example, sort in reverse order
		ints.sort((o1,o2) -> {return (o2-o1);});
		System.out.println("Sorted in Decreasing Order: "+ints);
		
	}
	
	class MyComparator implements Comparator<Integer>{
		@Override
		public int compare(Integer o1, Integer o2) {
			return (o1 - o2);
		}	
	}
}

线程安全ArrayList

Java ArrayList不是线程安全的。
因此,如果您在多线程环境中工作,请使用以下代码获取线程安全的ArrayList。

Original List: [580, 855, 889, 858, 536, 842, 223, 405, 854, 354]
Sorted in Increasing Order: [223, 354, 405, 536, 580, 842, 854, 855, 858, 889]
Sorted in Decreasing Order: [889, 858, 855, 854, 842, 580, 536, 405, 354, 223]

在这种情况下,您还可以使用CopyOnWriteArrayList并发集合类。