Java集合元素ArrayList删除问题

摘要

Java中的集合框架ArrayList是我们最常用的一个类,ArrayList实现了List接口,可以重复存储数据,可以动态的增加和删除元素,下面介绍一下在删除ArrayList元素问题。

Java中的集合框架ArrayList是我们最常用的一个类,ArrayList实现了List接口,可以重复存储数据,可以动态的增加和删除元素,下面介绍一下在删除ArrayList元素问题。

ArrayList实现了List接口,内部通过Object类型的数组有序存储数据(可重复),并且能够根据元素数量进行扩容,实现了动态的增加和减少元素.

 

1.常用方法

add()

addAll()

增加元素

remove()

removeAll()

clear()

移除元素

contains()

containsAll()

是否包含指定元素

size()

isEmpty()

equals()

元素个数

是否为空

是否为同一个ArrayList

iterator()

toArray()

创建迭代器

将元素返回指定数组

 

2关于遍历

Java代码  收藏代码

  1. public class ArrayListDemo2 {  

  2.   

  3.     public static void main(String[] args) {  

  4.   

  5.         ArrayList<Integer> list = new ArrayList<>();  

  6.         list.add(23);  

  7.         list.add(33);  

  8.         list.add(64);  

  9.         list.add(33);  

  10.         list.add(54);  

  11.         list.add(89);  

  12.   

  13.         // 重写了toString方法,直接打印输出  

  14.         System.out.println(list);  

  15.           

  16.         // 通过for循环遍历  

  17.         for (int i = 0, j = list.size(); i < j; i++) {  

  18.             System.out.println(list.get(i));  

  19.         }  

  20.   

  21.         // 通过for each遍历  

  22.         for (int value : list) {  

  23.             System.out.println(value);  

  24.         }  

  25.   

  26.         // 通过迭代器遍历  

  27.         Iterator<Integer> car = list.iterator();  

  28.         while (car.hasNext()) {  

  29.             int value = car.next();  

  30.             System.out.println(value);  

  31.         }  

  32.     }  

  33. }  

 

3.关于删除

Java代码  收藏代码

  1. public class ArrayListDemo3 {  

  2.   

  3.     public static void main(String[] args) {  

  4.           

  5.         ArrayList<String> list = new ArrayList<>();  

  6.         String str1 = new String("Hello");  

  7.         String str2 = new String("Hello");  

  8.         list.add(str1);  

  9.         System.out.println(list.size());  

  10.         list.remove(str2);  

  11.         System.out.println(list.size());  

  12.     }  

  13. }  

运行结果:

1

0

虽然str1和str2是两个String对象,但是list中的元素被移除了.实际上remove方法内部是通过equals方法判断是否是同一个元素,我们用下面的代码验证:

Java代码  收藏代码

  1. public class ArrayListDemo3 {  

  2.   

  3.     public static void main(String[] args) {  

  4.   

  5.         ArrayList<Person> list = new ArrayList<>();  

  6.         Person p1 = new Person("Hello");  

  7.         Person p2 = new Person("World");  

  8.         list.add(p1);  

  9.         System.out.println(list.size());  

  10.         list.remove(p2);  

  11.         System.out.println(list.size());  

  12.     }  

  13. }  

  14.   

  15. class Person {  

  16.     String name;  

  17.     public Person(String name) {  

  18.         this.name = name;  

  19.     }  

  20.     @Override  

  21.     public boolean equals(Object o) {  

  22.         System.out.println("我被执行了");  

  23.         return true;  

  24.     }  

  25. }  

运行结果:

1

我被执行了

0

 

4.关于删除

我们利用一个ArrayList来存储分数,现在希望将60分以下的删除:

Java代码  收藏代码

  1. public class ArrayListDemo4 {  

  2.   

  3.     public static void main(String[] args) throws Exception {  

  4.   

  5.         ArrayList<Integer> list = new ArrayList<>();  

  6.         list.add(80);  

  7.         list.add(54);  

  8.         list.add(68);  

  9.         list.add(72);  

  10.         list.add(49);  

  11.         list.add(51);  

  12.         list.add(98);  

  13.         list.add(77);  

  14.         list.add(43);  

  15.         list.add(50);  

  16.   

  17.         for (int i = 0; i < list.size(); i++) {  

  18.             if (list.get(i) < 60) {  

  19.                 list.remove(i);  

  20.             }  

  21.         }  

  22.   

  23.         for (int value : list) {  

  24.             System.out.println(value);  

  25.         }  

  26.     }  

  27. }  

 运行结果:

80

68

72

51

98

77

50

为什么51和50没有删掉呢?实际上,ArrayList在每次删除一个元素后,后面的元素会向前移动补位.在上述程序中,当我们删除49后,后面的元素向前补位,下一次循环时索引加1,便跳过了51.解决方法:for循环从ArrayList尾部向前遍历. 

Java代码  收藏代码

  1. for (int i = list.size() - 1; i >= 0; i--) {  

  2.     if (list.get(i) < 60) {  

  3.         list.remove(i);  

  4.     }  

  5. }  

运行结果:

80
68
72
98
77

 

5.关于删除

在上一节中,我们能不能通过for each和Iterator进行操作呢?

Java代码  收藏代码

  1. // for each循环  

  2. for (int value : list) {  

  3.     if (value < 60) {  

  4.         list.remove(new Integer(value));  

  5.     }  

  6. }  

  7. // 迭代器  

  8. Iterator<Integer> car = list.iterator();  

  9. while (car.hasNext()) {  

  10.     int value = car.next();  

  11.     if (value < 60) {  

  12.         list.remove(new Integer(value));  

  13.     }  

  14. }  

两段代码的运行结果一样:java.util.ConcurrentModificationException.抛出了并发修改异常,所以当for each和iterator对一个ArrayList进行迭代时,需保证这个ArrayList不能改变,因为ArrayList是非线程安全的.但是Iterato提供了一个remove方法,可以对ArrayList中的元素进行删除.即:

Java代码  收藏代码

  1. Iterator<Integer> car = list.iterator();  

  2. while (car.hasNext()) {  

  3.     int value = car.next();  

  4.     if (value < 60) {  

  5.         car.remove();  

  6.     }  

  7. }  

运行结果:  
80
68
72
98
77

 

6.关于扩容

每一个ArrayList都有一个容量,即数组Object[] elementData的长度.利用无参构造函数创建一个ArrayList时,会使用默认容量10.当我们向其中不断地增加元素超过容量时,ArrayList会进行扩容.ArrayList的扩容原理:新建一个Object类型的数组,长度为原数组长度*3/2,然后将就数组的值赋给新数组,使elementData指向新数组.通过反射进行验证:

Java代码  收藏代码

  1. public class ArrayListDemo6 {  

  2.   

  3.     public static void main(String[] args) throws Exception {  

  4.           

  5.         ArrayList<Integer> list = new ArrayList<>();  

  6.         Class c = Class.forName("java.util.ArrayList");  

  7.         Field f = c.getDeclaredField("elementData");  

  8.         f.setAccessible(true);  

  9.         Object[] data = null;  

  10.   

  11.         // 向list中添加50个元素,输出每次ElementData的长度  

  12.         for (int i = 1; i <= 50; i++) {  

  13.             list.add(i);  

  14.             data = (Object[]) f.get(list);  

  15.             System.out.print(data.length + "\t");  

  16.             if (i % 10 == 0) {  

  17.                 System.out.println();  

  18.             }  

  19.         }  

  20.     }  

  21. }  

 运行结果:

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代码  收藏代码

  1. private static int hugeCapacity(int minCapacity) {  

  2.     if (minCapacity < 0)  

  3.         throw new OutOfMemoryError();  

  4.     return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE  

  5.             : MAX_ARRAY_SIZE;  

  6. }  

所以,当ArrayList达到极限容量时,再次扩容会抛出异常.

 

8.1.8版本新特性

JDK在SE1.8中新增了stream包,并且在Collection中增加了stream()方法,利用其特性我们可以用来进行过滤以及排序等.

Java代码  收藏代码

  1. public class ArrayListDemo8 {  

  2.   

  3.     public static void main(String[] args) {  

  4.         ArrayList<Integer> list = new ArrayList<>();  

  5.         Collections.addAll(list, 953467892379661298);  

  6.         list.stream().filter(e -> e > 60).sorted((i1, i2) -> i1 - i2).forEach(System.out::println);  

  7.     }  

  8. }  

上述代码执行功能:过滤掉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为基数


IT家园
IT家园

网友最新评论 (0)