概述
在这个教程中,我们将探讨Java中的List
接口。我们将讨论List
提供的方法、其实现以及使用场景。
Java列表简介
Java是一种面向对象的语言,大多数问题都涉及对象及其关联的行为或操作。当我们需要同时处理同一类型多个对象时,就需要用到集合。在Java中,List
(此处链接)是一种保证元素顺序并允许重复的集合实现。
列表方法与使用
让我们来看看List
接口最重要的方法,并了解如何使用它们。这里以ArrayList
实现为例。
3.1 添加元素
使用void add(E element)
方法向列表添加新元素:
@Test
public void givenAFruitList_whenAddNewFruit_thenFruitIsAdded(){
List fruits = new ArrayList();
assertEquals("Unexpected number of fruits in the list, should have been 0", 0, fruits.size());
fruits.add("Apple");
assertEquals("Unexpected number of fruits in the list, should have been 1", 1, fruits.size());
}
3.2 检查列表是否包含元素
我们可以使用boolean contains(Object o)
方法检查列表是否包含特定元素:
@Test
public void givenAFruitList_whenContainsFruit_thenFruitIsInTheList(){
List fruits = new ArrayList();
fruits.add("Apple");
assertTrue("Apple should be in the fruit list", fruits.contains("Apple"));
assertFalse("Banana should not be in the fruit list", fruits.contains("Banana"));
}
3.3 检查列表是否为空
使用boolean isEmpty()
方法检查列表是否为空:
@Test
public void givenAnEmptyFruitList_whenEmptyCheck_thenListIsEmpty(){
List fruits = new ArrayList();
assertTrue("Fruit list should be empty", fruits.isEmpty());
fruits.add("Apple");
assertFalse("Fruit list should not be empty", fruits.isEmpty());
}
3.4 遍历列表
如果我们想遍历列表,可以使用ListIterator listIterator()
方法:
@Test
public void givenAFruitList_whenIterateOverIt_thenFruitsAreInOrder(){
List fruits = new ArrayList();
fruits.add("Apple"); // fruit at index 0
fruits.add("Orange");// fruit at index 1
fruits.add("Banana");// fruit at index 2
int index = 0;
for (Iterator it = fruits.listIterator(); it.hasNext(); ) {
String fruit = it.next();
assertEquals("Fruits should be in order", fruits.get(index++), fruit);
}
}
3.5 删除元素
使用boolean remove(Object o)
方法从列表中删除元素:
@Test
public void givenAFruitList_whenRemoveFruit_thenFruitIsRemoved(){
List fruits = new ArrayList();
fruits.add("Apple");
fruits.add("Orange");
assertEquals("Unexpected number of fruits in the list, should have been 2", 2, fruits.size());
fruits.remove("Apple");
assertEquals("Unexpected number of fruits in the list, should have been 1", 1, fruits.size());
}
3.6 修改元素
使用E set(int index, E element)
方法在指定索引处修改列表中的元素:
@Test
public void givenAFruitList_whenSetFruit_thenFruitIsUpdated(){
List fruits = new ArrayList();
fruits.add("Apple");
fruits.add("Orange");
fruits.set(0, "Banana");
assertEquals("Fruit at index 0 should be Banana", "Banana", fruits.get(0));
}
3.7 获取列表大小
使用int size()
方法获取列表的大小:
List fruits = new ArrayList();
fruits.add("Apple");
fruits.add("Orange");
assertEquals("Unexpected number of fruits in the list, should have been 2", 2, fruits.size());
3.8 排序列表
我们有许多方法对列表进行排序。这里,我们将看看如何使用List
接口的default void sort(Comparator c)
方法来实现。
这个方法需要一个比较器作为参数。我们可以提供自然顺序的比较器:
@Test
public void givenAFruitList_whenSort_thenFruitsAreSorted(){
List fruits = new ArrayList();
fruits.add("Apple");
fruits.add("Orange");
fruits.add("Banana");
fruits.sort(Comparator.naturalOrder());
assertEquals("Fruit at index 0 should be Apple", "Apple", fruits.get(0));
assertEquals("Fruit at index 1 should be Banana", "Banana", fruits.get(1));
assertEquals("Fruit at index 2 should be Orange", "Orange", fruits.get(2));
}
3.9 创建子列表
通过向List
的subList(int fromIndex, int toIndex)
方法提供fromIndex
和toIndex
参数,可以从列表中创建子列表。需要注意的是,toIndex
不包括在内:
@Test
public void givenAFruitList_whenSublist_thenWeGetASublist(){
List fruits = new ArrayList();
fruits.add("Apple");
fruits.add("Orange");
fruits.add("Banana");
List fruitsSublist = fruits.subList(0, 2);
assertEquals("Unexpected number of fruits in the sublist, should have been 2", 2, fruitsSublist.size());
assertEquals("Fruit at index 0 should be Apple", "Apple", fruitsSublist.get(0));
assertEquals("Fruit at index 1 should be Orange", "Orange", fruitsSublist.get(1));
}
3.10 将列表转换为数组
我们可以使用T[] toArray(T[] a)
方法创建一个包含列表元素的数组:
@Test
public void givenAFruitList_whenToArray_thenWeGetAnArray(){
List fruits = new ArrayList();
fruits.add("Apple");
fruits.add("Orange");
fruits.add("Banana");
String[] fruitsArray = fruits.toArray(new String[0]);
assertEquals("Unexpected number of fruits in the array, should have been 3", 3, fruitsArray.length);
assertEquals("Fruit at index 0 should be Apple", "Apple", fruitsArray[0]);
assertEquals("Fruit at index 1 should be Orange", "Orange", fruitsArray[1]);
assertEquals("Fruit at index 2 should be Banana", "Banana", fruitsArray[2]);
}
4. List
实现
让我们看看Java中常用的List
接口实现。
4.1 ArrayList
ArrayList
是List
接口的可变数组实现。它实现了所有可选操作,并允许所有元素,包括null
。这个类大致相当于Vector
,但它是无同步的。
这是List
接口最常用的实现。
4.2 CopyOnWriteArrayList
CopyOnWriteArrayList
是ArrayList
的线程安全版本。这个类的所有修改操作(如添加、设置等)都会在底层数组上创建一个新的副本。
这个实现因其内置的线程安全能力而被使用。
4.3 LinkedList
LinkedList
是List
和Deque
接口的双向链表实现。它实现了所有可选操作,并允许所有元素(包括null
)。
4.4 抽象列表实现
这里有两个抽象实现,提供了List
接口的骨架实现,帮助减少扩展和定制List
所需的工作:
-
AbstractList
- 用于内部状态的“随机访问”数据存储(如数组) -
AbstractSequentialList
- 用于内部状态的“顺序访问”数据存储(如链表)
4.5 其他具体列表实现
以下是两个值得讨论的具体实现:
-
Vector
- 实现了一个可增长的对象数组。像数组一样,它使用整数索引包含组件。这个类是同步的。如果不需要线程安全的实现,建议使用ArrayList
替换Vector
(详情)。 -
Stack
- 表示一个后进先出(LIFO)对象栈。它继承自Vector
类,并提供了五个额外的操作,使向量可以被视为栈。
Java还提供了一些特定的List
实现,它们的行为类似于上述讨论的一种实现。
5. 总结
在这篇文章中,我们深入研究了Java的List
接口及其实现。当我们只关心元素顺序并允许重复时,List
是首选的集合类型。由于它们内部处理扩容,因此比数组更受欢迎。
如往常一样,代码片段可以在GitHub上找到。