Java中的集合框架ArrayList是我们最常用的一个类,ArrayList实现了List接口,可以重复存储数据,可以动态的增加和删除元素,下面介绍一下在删除ArrayList元素问题。
ArrayList实现了List接口,内部通过Object类型的数组有序存储数据(可重复),并且能够根据元素数量进行扩容,实现了动态的增加和减少元素.
1.常用方法
| add() addAll() | 增加元素 | 
| remove() removeAll() clear() | 移除元素 | 
| contains() containsAll() | 是否包含指定元素 | 
| size() isEmpty() equals() | 元素个数 是否为空 是否为同一个ArrayList | 
| iterator() toArray() | 创建迭代器 将元素返回指定数组 | 
2关于遍历
Java代码  
- 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.关于删除
Java代码  
- 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方法判断是否是同一个元素,我们用下面的代码验证:
Java代码  
- 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分以下的删除:
Java代码  
- 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没有删掉呢?实际上,ArrayList在每次删除一个元素后,后面的元素会向前移动补位.在上述程序中,当我们删除49后,后面的元素向前补位,下一次循环时索引加1,便跳过了51.解决方法:for循环从ArrayList尾部向前遍历.
Java代码  
- for (int i = list.size() - 1; i >= 0; i--) { 
- if (list.get(i) < 60) { 
- list.remove(i); 
- } 
- } 
运行结果:
80
68
72
98
77
5.关于删除
在上一节中,我们能不能通过for each和Iterator进行操作呢?
Java代码  
- // 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是非线程安全的.但是Iterato提供了一个remove方法,可以对ArrayList中的元素进行删除.即:
Java代码  
- Iterator<Integer> car = list.iterator(); 
- while (car.hasNext()) { 
- int value = car.next(); 
- if (value < 60) { 
- car.remove(); 
- } 
- } 
运行结果:  
80
68
72
98
77
6.关于扩容
每一个ArrayList都有一个容量,即数组Object[] elementData的长度.利用无参构造函数创建一个ArrayList时,会使用默认容量10.当我们向其中不断地增加元素超过容量时,ArrayList会进行扩容.ArrayList的扩容原理:新建一个Object类型的数组,长度为原数组长度*3/2,然后将就数组的值赋给新数组,使elementData指向新数组.通过反射进行验证:
Java代码  
- 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是否发生溢出,关键代码:
Java代码  
- 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()方法,利用其特性我们可以用来进行过滤以及排序等.
Java代码  
- 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<E>,每一个Node有三个属性:
| E item; | 元素本身 | 
| Node<E> next; | 指向后一个Node | 
| Node<E> prev; | 指向前一个Node | 
而LinkedList本身只包含两个Node类型的属性first和last,通过Node使元素形成链表,并且是双向的,实现逻辑上的地址连续.在进行插入和删除功能时,性能要优于ArrayList.
3)Stack
对象堆栈.遵循LIFO(Last In First Out)原则,常用方法:
| empty() | 测试堆栈是否为空 | 
| peek() | 查看堆栈顶部的对象,但不移除 | 
| pop() | 移除堆栈顶部的对象,并返回该对象 | 
| push() | 把对象压入堆栈顶部 | 
| search() | 返回对象在堆栈中的位置,以1为基数 | 






