Java中的集合框架ArrayList是我們最常用的一個類,ArrayList實現了List接口,可以重複存儲數據,可以動態的增加和刪除元素,下面介紹一下在刪除ArrayList元素問題。
ArrayList實現了List接口,內部通過Object類型的數組有序存儲數據(可重複),並且能夠根據元素數量進行擴容,實現了動態的增加和減少元素.
1.常用方法
增加元素
add()
addAll()
移除元素
remove()
removeAll()
clear()
是否包含指定元素
contains()
containsAll()
元素個數
size()
是否爲空
isEmpty()
是否爲同一個ArrayList
equals()
創建迭代器
iterator()
將元素返回指定數組
toArray()
2、關於遍歷
public class ArrayListDemo2 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(23);
list.add(33);
list.add(64);
list.add(33);
list.add(54);
list.add(89);
// 重寫了toString方法,直接打印輸出
System.out.println(list);
// 通過for循環遍歷
for (int i = 0, j = list.size(); i < j; i++) {
System.out.println(list.get(i));
}
// 通過for each遍歷
for (int value : list) {
System.out.println(value);
}
// 通過迭代器遍歷
Iterator<Integer> car = list.iterator();
while (car.hasNext()) {
int value = car.next();
System.out.println(value);
}
}
}
3.關於刪除
public class ArrayListDemo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
String str1 = new String("Hello");
String str2 = new String("Hello");
list.add(str1);
System.out.println(list.size());
list.remove(str2);
System.out.println(list.size());
}
}
運行結果:
1
0
雖然str1和str2是兩個String對象,但是list中的元素被移除了.實際上remove方法內部是通過equals方法判斷是否是同一個元素,我們用下面的代碼驗證:
public class ArrayListDemo3 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
Person p1 = new Person("Hello");
Person p2 = new Person("World");
list.add(p1);
System.out.println(list.size());
list.remove(p2);
System.out.println(list.size());
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
System.out.println("我被執行了");
return true;
}
}
運行結果:
1
我被執行了
0
4.關於刪除
我們利用一個ArrayList來存儲分數,現在希望將60分以下的刪除:
public class ArrayListDemo4 {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(80);
list.add(54);
list.add(68);
list.add(72);
list.add(49);
list.add(51);
list.add(98);
list.add(77);
list.add(43);
list.add(50);
for (int i = 0; i < list.size(); i++) {
if (list.get(i) < 60) {
list.remove(i);
}
}
for (int value : list) {
System.out.println(value);
}
}
}
運行結果:
80
68
72
51
98
77
50
爲什麼51和50沒有for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) < 60) {
list.remove(i);
}
}
運行結果:
80
68
72
98
77刪掉呢?實際上,ArrayList在每次刪除一個元素後,後面的元素會向前移動補位.在上述程序中,當我們刪除49後,後面的元素向前補位,下一次循環時索引加1,便跳過了51.解決方法:for循環從ArrayList尾部向前遍歷.
5.關於刪除
在上一節中,我們能不能通過for each和Iterator進行操作呢?
// for each循環
for (int value : list) {
if (value < 60) {
list.remove(new Integer(value));
}
}
// 迭代器
Iterator<Integer> car = list.iterator();
while (car.hasNext()) {
int value = car.next();
if (value < 60) {
list.remove(new Integer(value));
}
}
兩段代碼的運行結果一樣:java.util.ConcurrentModificationException.拋出了併發修改異常,所以當for each和iterator對一個ArrayList進行迭代時,需保證這個ArrayList不能改變,
因爲ArrayList是非線程安全的.但是
1、Iterato提供了一個remove方法,可以對ArrayList中的元素進行刪除,
2、還可以使用參考Arraylist來達到刪除效果,
3、也可使用CopyOnWriteArrayList支持併發訪問.即:
Java代碼
a、
Iterator<Integer> car = list.iterator();
while (car.hasNext()) {
int value = car.next();
if (value < 60) {
car.remove();
}
}
運行結果:
80
68
72
98
77
b、
private void referenceRemoveMethod(List<bean> list)
{
List<bean> referenceList = new ArrayList<bean>();
referenceList.addAll(list);
for (int i = 0; i < referenceList.size(); i++)
{
if (referenceList.get(i).getName().equals("A"))
{
list.remove(referenceList.get(i));
}
}
}
6.關於擴容
每一個ArrayList都有一個容量,即數組Object[] elementData的長度.利用無參構造函數創建一個ArrayList時,會使用默認容量10.當我們向其中不斷地增加元素超過容量時,ArrayList會進行擴容.ArrayList的擴容原理:新建一個Object類型的數組,長度爲原數組長度*3/2,然後將就數組的值賦給新數組,使elementData指向新數組.通過反射進行驗證:
public class ArrayListDemo6 {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
Class c = Class.forName("java.util.ArrayList");
Field f = c.getDeclaredField("elementData");
f.setAccessible(true);
Object[] data = null;
// 向list中添加50個元素,輸出每次ElementData的長度
for (int i = 1; i <= 50; i++) {
list.add(i);
data = (Object[]) f.get(list);
System.out.print(data.length + "\t");
if (i % 10 == 0) {
System.out.println();
}
}
}
}
運行結果:
10 10 10 10 10 10 10 10 10 10
15 15 15 15 15 22 22 22 22 22
22 22 33 33 33 33 33 33 33 33
33 33 33 49 49 49 49 49 49 49
49 49 49 49 49 49 49 49 49 73
如果我們已知要存儲數據的容量,儘量在創建ArrayList時爲其指定容量,避免其多次擴容而降低性能.另外,ArrayList中還爲我們提供了兩個針對於控制容量的方法:
trimToSize() 將elementData的長度修整爲當前元素個數
ensureCapacity() 設置當前ArrayList容量
7.關於最大容量
在ArrayList中,還有一個靜態常量MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,用來限制當前ArrayList的最大容量(理論上可以達到Integer.MAX_VALUE).在每次進行擴容時,會用一個int型數據來存儲新的容量:int newCapacity = oldCapacity + (oldCapacity >> 1);當ArrayList中的元素達到MAX_ARRAY_SIZE後再次擴充時,newCapacity經過擴容算法會溢出,導致符號位由0變1,變成負值.ArrayList中的hugeCapacity方法通過判斷newCapacity是否發生溢出,關鍵代碼:
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
所以,當ArrayList達到極限容量時,再次擴容會拋出異常.
8.1.8版本新特性
JDK在SE1.8中新增了stream包,並且在Collection中增加了stream()方法,利用其特性我們可以用來進行過濾以及排序等.
public class ArrayListDemo8 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 95, 34, 67, 89, 23, 79, 66, 12, 98);
list.stream().filter(e -> e > 60).sorted((i1, i2) -> i1 - i2).forEach(System.out::println);
}
}
上述代碼執行功能:過濾掉60及以下的分數→排序→打印輸出.
運行結果:
66
67
79
89
95
98
9.List的其他實現類
1)Vector
Vector是早期的collection,與ArrayList的區別主要有兩點:線程安全,擴容方式*2.
2)LinkedList
雙向循環鏈表,內部元素不是通過數組存儲的.而是把每一個元素封裝Node,每一個Node有三個屬性:
E item; 元素本身
Node next; 指向後一個Node
Node prev; 指向前一個Node
而LinkedList本身只包含兩個Node類型的屬性first和last,通過Node使元素形成鏈表,並且是雙向的,實現邏輯上的地址連續.在進行插入和刪除功能時,性能要優於ArrayList.
3)Stack
對象堆棧.遵循LIFO(Last In First Out)原則,常用方法:
empty() 測試堆棧是否爲空
peek() 查看堆棧頂部的對象,但不移除
pop() 移除堆棧頂部的對象,並返回該對象
push() 把對象壓入堆棧頂部
search() 返回對象在堆棧中的位置,以1爲基數