List 代表有顺序的一组元素,顺序代表遍历元素时是有顺序的,先放进 List 的元素会先被遍历到,这点很像数组,但是跟数组很不一样的是 List 对大小没有限制。
List 是使用 Java 编写程序的时候,最高频使用的数据结构,今天这篇文章我们就来好好地把List主要的使用方法过一遍,在后半部分还会给出多个特遍有用的能提高我们开发效率的实践示例,整个文章的大纲如下:
List 接口
List 接口的全名叫 java.util.List,其中定义的方法如下图所示:
这就意味着,只要类实现了这些方法,那么它就是一个 List 接口的实现类。
Java List 中包含的元素可以根据它们在 Java List 内部出现的顺序进行插入、访问、迭代和删除。元素的顺序是这个数据结构被称为 List 的原因。 Java List 中的每个元素都有一个索引,列表中的第一个元素的索引为 0,第二个元素的索引为 1,以此类推。索引的意思是“距离列表开头有多少个元素”。
下面我们用数组作为底层存储的结构,自己实现一个List类。
package com.example.learncollection;
import java.util.*;
public class MyArrayList implements List {
private Object[] elements;
private int curr;
// 先给数组分配16个长度
public MyArrayList() {
elements = new Object[16];
curr = 0;
}
@Override
public int size() {
return curr;
}
@Override
public boolean isEmpty() {
return curr == 0;
}
@Override
public boolean contains(Object o) {
for (Object ele : elements) {
if (Objects.equals(ele, o)) {
return true;
}
}
return false;
}
@Override
public void clear() {
curr = 0;
}
@Override
public Object get(int index) {
if (index > curr || index < 0) {
throw new IndexOutOfBoundsException("out of bound " + curr + " for " + index);
}
return elements[index];
}
@Override
public boolean add(Object o) {
if (curr == elements.length - 1) {
// 数组满了,扩容一倍,把数据拷贝到新数组里。
Object[] temp = new Object[elements.length * 2];
System.arraycopy(elements, 0, temp, 0, elements.length);
elements = temp;
}
elements[curr] = o;
curr++;
return true;
}
@Override
public Iterator iterator() {
throw new UnsupportedOperationException();
}
@Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}
...... // 其他方法都抛出 UnsupportedOperationException 异常实现,这里省略。
}
上面只实现了几个基础的方法,其他操作的方法都按抛出 UnsupportedOperationException 异常来实现,在例程里省略掉了。
package com.example.learncollection;
import java.util.List;
public class UseListAppMain {
public static void main(String[] args) {
List myList = new MyArrayList();
for (int i = 0; i < 10; i++) {
myList.add("str" + (i % 5));
}
System.out.println();
System.out.println("输出" + myList.getClass() + "中的元素,共" + myList.size() + "个");
for (List element : myList) {
System.out.println(element);
}
}
}
复制代码
Java 提供的List实现类
上面解释 List 接口的时候,尝试自己写了一个实现类,能观察到需要实现的方法有很多,且 List 的动态扩容和查找实现的都很晦涩,也毫无性能可言。好在 Java 里给我们提供了很多完备的 List 实现类,我们直接用就行了,不必自己编写类实现 List 接口。
可以在 Java Collections API 中的以下 List 实现之间进行选择:
- java.util.ArrayList java
- util.LinkedList
- java.util.Vector
- java.util.Stack
在这些实现中,ArrayList 是最常用的。 java.util.concurrent 包中还有并发 List 的实现。这部分内容等到并发相关的章节再详细解释。
创建列表实例
可以通过实例化实现了List接口的类,创建一个列表实例。
List listA = new ArrayList();
List listB = new LinkedList();
List listC = new Vector();
List listD = new Stack();
复制代码
大多数情况下我们都会使用 ArrayList 类,但在某些情况下,使用其他实现之一可能更有意义。
默认情况下,可以往 List 中放入任何对象。但是从 Java 5 开始,使用 Java 泛型可以限制插入到列表中的对象类型。下面是一个例子:
List<MyObject> list = new ArrayList<ObjectType>();
复制代码
现在这个列表只允许插入 MyObject 类的实例,这样一来访问和迭代列表元素的时候就不需要再对其进行强制类型转换了。
List<MyObject> lis