Java 基础之 List 深度探秘

List 是什么?

List 集合是 Java 集合框架中的一种有序、可重复的数据结构,它继承自 Collection 接口,允许存储多个元素。与数组不同,List 集合的大小是动态可变的,可以根据需要动态地添加或删除元素。

List 集合中的元素允许重复,各元素的顺序是对象插入的顺序,用户可通过使用索引(元素在集合中的位置)来访问集合中的元素。Java 集合就像一个容器,可以存储任何类型的数据,也可以结合泛型来存储具体的类型对象。在程序运行时,Java 集合可以动态的进行扩展,随着元素的增加而扩大。在 Java 中,集合类通常存在于 java.util 包中。Java 集合主要由 2 大体系构成,分别是 Collection 体系和 Map 体系,其中 Collection 和 Map 分别是 2 大体系中的顶层接口。Collection 主要有三个子接口,分别为 List(列表)、Set(集)、Queue(队列)。其中,List、Queue 中的元素有序可重复,而 Set 中的元素无序不可重复。List 中主要有 ArrayList、LinkedList 两个实现类;Set 中则是有 HashSet 实现类;而 Queue 是在 JDK1.5 后才出现的新集合,主要以数组和链表两种形式存在。Map 同属于 java.util 包中,是集合的一部分,但与 Collection 是相互独立的,没有任何关系。Map 中都是以 key-value 的形式存在,其中 key 必须唯一,主要有 HashMap、HashTable、treeMap 三个实现类。

在 Collection 中,List 集合是有序的,可对其中每个元素的插入位置进行精确地控制,可以通过索引来访问元素,遍历元素。在 List 集合中,我们常用到 ArrayList 和 LinkedList 这两个类。

  1. ArrayList 集合
    • ArrayList 底层通过数组实现,随着元素的增加而动态扩容。ArrayList 是 Java 集合框架中使用最多的一个类,是一个数组队列,线程不安全集合。它继承于 AbstractList,实现了 List, RandomAccess, Cloneable, Serializable 接口。
    • ArrayList 实现 List,得到了 List 集合框架基础功能;实现 RandomAccess,获得了快速随机访问存储元素的功能,RandomAccess 是一个标记接口,没有任何方法;实现 Cloneable,得到了 clone() 方法,可以实现克隆功能;实现 Serializable,表示可以被序列化,通过序列化去传输,典型的应用就是 hessian 协议。
    • ArrayList 集合的特点:容量不固定,随着容量的增加而动态扩容(阈值基本不会达到);有序集合(插入的顺序 == 输出的顺序);插入的元素可以为 null;增删改查效率更高(相对于 LinkedList 来说);线程不安全。
    • ArrayList 的底层数据结构:数组。
  1. LinkedList 集合
    • LinkedList 底层通过链表来实现,随着元素的增加不断向链表的后端增加节点。LinkedList 是一个双向链表,每一个节点都拥有指向前后节点的引用。相比于 ArrayList 来说,LinkedList 的随机访问效率更低。
    • 它继承 AbstractSequentialList,实现了 List, Deque, Cloneable, Serializable 接口。实现 List,得到了 List 集合框架基础功能;实现 Deque,Deque 是一个双向队列,也就是既可以先入先出,又可以先入后出,说简单点就是既可以在头部添加元素,也可以在尾部添加元素;实现 Cloneable,得到了 clone() 方法,可以实现克隆功能;实现 Serializable,表示可以被序列化,通过序列化去传输,典型的应用就是 hessian 协议。
    • LinkedList 集合的底层数据结构:链表。

二、常见的 List 实现类

1. ArrayList

  1. 基于动态数组实现,支持快速随机访问,适用于读取操作频繁的场景。

ArrayList 是 Java 集合框架中的一个类,它实现了 List 接口,底层基于数组实现。数据在内存中是连续存储的,因此支持随机访问非常快速。在列表末尾添加/删除元素非常快,但在列表中间插入/删除元素可能需要移动元素,较慢。每次自动增长时,数组大小增加 50%。

2. LinkedList

  1. 基于双向链表实现,支持高效的插入和删除操作,适用于频繁插入、删除元素的场景。

LinkedList 也是 Java 集合框架中的一个类,同样实现了 List 接口,但底层基于链表实现。每个元素都包含指向前后元素的指针,插入和删除操作非常高效。但随机访问性能较差,需要从头或尾开始遍历链表。提供了额外的方法和接口,如 Deque,可以作为队列、双端队列或栈使用。

3. Vector

  1. 类似 ArrayList,但是线程安全,性能相对较低,一般不推荐使用。

Vector 的底层与 ArrayList 类似,都是以动态数组的方式进行对象的存储。但 Vector 是线程同步操作安全的,很多对外的方法都用 Synchronized 关键字进行修饰,所以通过 vector 进行操作性能并不高。每次自动增长时,数组大小增加一倍,或者增加指定的大小。在工作中,如果需要将集合(ArrayList 与 LinkedList)转换为线程安全,可以用到 Collection 工具类。但有更好的实现线程安全的替代方法,不建议使用 Vector。

三、List 的常见用法

1. 创建和基本操作

  1. 通过示例展示如何创建一个 ArrayList,以及进行添加、获取、删除元素等基本操作。

在 Java 中,创建一个 ArrayList 可以按照以下步骤进行:

  1. 导入包:import java.util.ArrayList;。
  1. 创建引用类型的变量:数据类型<集合存储的数据类型> 变量名 = new 数据类型<集合存储的数据类型>();。例如,创建一个存储整数的 ArrayList:ArrayList<Integer> arr = new ArrayList<Integer>();。注意,集合存储的数据类型必须是引用类型,不能是基本数据类型,如果要存储整数,可以使用Integer而不是int。
  1. 使用变量名.方法进行操作。

添加元素:可以使用add(参数)方法向集合中添加元素。例如,arr.add(1);arr.add(2);arr.add(3);。还可以使用add(int 索引,存储的元素)将元素添加到指定的索引上。比如,arr.add(1, 10);会将数字 10 添加到索引为 1 的位置。

获取元素:使用get(int index)方法取出集合中的元素。参数为索引,例如System.out.println(arr.get(0));会输出集合中索引为 0 的元素。

删除元素:有多种方法可以删除元素。

  • remove(int 索引)可以删除指定索引上的元素。例如,arr.remove(1);会删除索引为 1 的元素。
  • remove(Object element)可以删除集合中第一次出现的指定元素。例如,boolean s1 = arr.remove("a");如果集合中有字符串"a",则会删除它并返回true。

集合的遍历:可以使用多种方式遍历 ArrayList。

  • for循环:for(int i=0;i<arr.size();i++){System.out.println(arr.get(i));}。
  • 加强for循环(也称为foreach循环):for(Integer element : arr){System.out.println(element);}。

此外,ArrayList 还有一些补充方法:

  • set(int 索引,修改后的元素)可以将指定索引的元素进行修改,并返回被覆盖的元素。
  • clear()可以清空集合中的所有元素。

四、List 集合的高级操作

1. 迭代操作

  1. 介绍使用迭代器(Iterator)和增强 for 循环对 List 进行迭代的方法。

在 Java 中,对 List 集合进行迭代有多种方法,其中包括使用迭代器(Iterator)和增强 for 循环。

使用迭代器(Iterator)进行迭代时,可以通过调用 List 的 iterator()方法获取一个迭代器。迭代器提供了一种统一的方式来遍历集合,它具有 hasNext()方法用来检测集合中是否还有元素,以及 next()方法用于获取下一个元素。例如:

复制代码

Iterator i = list.iterator();

while (i.hasNext()) {

Object obj = i.next();

if (obj.equals(2)) {

i.remove();

}

}

System.out.println(list);

增强 for 循环(也称为 foreach 循环)会自动迭代集合中的每个元素,使代码更加简洁。但是增强 for 循环不允许遍历时对集合进行操作,如果进行操作(如删除),会抛出异常 ConcurrentModificationException。操作一次后必须 break 结束循环,否则会抛出异常。例如:

复制代码

for (Object obj : list) {

if (obj.equals(2)) {

list.remove(obj);

break;

}

}

System.out.println(list);

此外,还可以使用普通的 for 循环来遍历 List 集合,通过索引访问每个元素。但在使用 for 循环遍历删除元素后,集合长度发生了变化,i 一直向后递增,需要注意处理。例如:

复制代码

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

list.remove(i);

}

System.out.println(list);

2. 列表操作

  1. 演示如何进行添加、删除、替换元素等列表操作。

在 Java 中,对 List 集合可以进行多种列表操作。

添加元素:可以使用add(参数)方法向集合中添加元素。例如,list.add(1);list.add(2);list.add(3);。还可以使用add(int 索引,存储的元素)将元素添加到指定的索引上。比如,list.add(1, 10);会将数字 10 添加到索引为 1 的位置。

删除元素:有多种方法可以删除元素。

  • remove(int 索引)可以删除指定索引上的元素。例如,list.remove(1);会删除索引为 1 的元素。
  • remove(Object element)可以删除集合中第一次出现的指定元素。例如,boolean s1 = list.remove("a");如果集合中有字符串"a",则会删除它并返回true。

替换元素:在集合中想使用某个元素替换另外的一个元素时,可能会出现一些常见错误。正常的替换(当替换的元素的索引小于需要替换的索引的时候)应该替换的位置,出现了不正常的替换(替换的元素的索引大于需要替换的索引),出现的结果就是使得替换的是替换的下一个索引的位置的元素,使得替换发生错误。还有可能出现越界异常(替换的索引到达集合的最大索引,当 remove 需要替换的元素的时候使得集合的长度 -1,当再次使用这个索引的时候会出现越界异常)。解决办法是在替换之前赋值,将替换的元素赋值到一个变量中,然后删除需要删除的元素,最后用这个变量替换到原来需要替换的位置。例如:

复制代码

public void copy(){

System.out.println("被淘汰的为"+minfitnesspos+" 被复制的染色体为"+maxfitnesspos);

List list = community.get(maxfitnesspos);

community.remove(minfitnesspos);

community.add(minfitnesspos,list);

for (int i =0; i < chromosomesize; i++) {

chromosomepos.get(minfitnesspos)[i] = chromosomepos.get(maxfitnesspos)[i];

}

3. 使用 Collections 工具类排序

  1. 展示如何使用 Collections 工具类对 List 进行排序。

Java 集合的工具类 Collections 中提供了两种对 List 集合进行排序的方法。

第一种称为自然排序,参与排序的对象需实现 comparable 接口,重写其 compareTo()方法,方法体中实现对象的比较大小规则。例如:

复制代码

package test;

public class Emp implements Comparable {

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public Emp() {

super();

}

public Emp(String name, int age) {

super();

this.name = name;

this.age = age;

}

@Override

public String toString() {

return "Emp [name=" + name + ", age=" + age + "]";

}

@Override

public int compareTo(Object o) {

if (o instanceof Emp) {

Emp emp = (Emp) o;

// return this.age-emp.getAge();//按照年龄升序排序

return this.name.compareTo(emp.getName());//换姓名升序排序

}

throw new ClassCastException("不能转换为 Emp 类型的对象...");

}

}

测试类中:

复制代码

package test;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import org.junit.BeforeClass;

import org.junit.Test;

public class TestSort {

static List list = new ArrayList();

@BeforeClass

public static void init(){

list.add(new Emp("tom",18));

list.add(new Emp("jack",20));

list.add(new Emp("rose",15));

list.add(new Emp("jerry",17));

System.out.println("排序前:");

for(Object o : list){

System.out.println(o);

}

}

@Test

public void testSortName(){

Collections.sort(list);

System.out.println("自然排序按 name 升序排序后:");

for(Object o : list){

System.out.println(o);

}

}

}

第二种叫定制排序,或自定义排序,需编写匿名内部类,先 new 一个 Comparator 接口的比较器对象 c,同时实现 compare()其方法;然后将比较器对象 c 传给 Collections.sort()方法的参数列表中,实现排序功能。例如:

复制代码

@Test

public void testComparatorSortAge(){

Collections.sort(list, new Comparator() {

@Override

public int compare(Object o1, Object o2) {

if (o1 instanceof Emp && o2 instanceof Emp) {

Emp e1 = (Emp) o1;

Emp e2 = (Emp) o2;

return e1.getAge() - e2.getAge();

}

throw new ClassCastException("不能转换为 Emp 类型");

}

});

System.out.println("使用 Comparator 比较器按 age 升序排序后:");

for(Object o : list){

System.out.println(o);

}

}

此外,还可以使用 Collections 工具类对包含基本数据类型的 List 进行排序。例如:

复制代码

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

public class SortList {

public static void sort1(){

List list = new ArrayList();

list.add(1);

list.add(3);

list.add(5);

list.add(3);

list.add(61);

list.add(11);

list.add(1);

list.add(2);

System.out.println(list);

Collections.sort(list);

System.out.println(list);

}

}

五、List 集合的高级特性

1. 同步性

  1. 讲解在多线程环境中使用 Collections.synchronizedList 方法创建同步 List 的方法。

在 Java 多线程环境中,为了确保 List 集合的线程安全,可以使用Collections.synchronizedList方法来创建同步的 List。语法如下:

复制代码

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

public class SynchronizedListTest {

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("IND");

list.add("AUS");

list.add("WI");

list.add("NZ");

list.add("ENG");

List<String> synlist = Collections.synchronizedList(list);

synchronized(synlist) {

Iterator<String> itr = synlist.iterator();

while(itr.hasNext()) {

String str = itr.next();

System.out.println(str);

}

}

}

}

使用Collections.synchronizedList方法时,为了保证串行访问,对后备列表的所有访问都必须通过返回的同步列表来完成。在迭代返回的列表时,用户必须手动同步它,因为在执行add()等方法的时候是加了synchronized关键字的,但是iterator()却没有加。

2. 使用 ListIterator

  1. 介绍 ListIterator 的强大功能,如在迭代过程中进行元素的修改、添加、删除等操作。

ListIterator是一个功能更强大的迭代器,它继承自Iterator接口,只能用于各种List类型的访问。ListIterator不仅可以像Iterator一样向后遍历,还可以向前遍历,并且可以在遍历的同时修改List的元素、获取遍历时游标的索引。

ListIterator常用的方法有:

  • hasNext():判断是否有下一个元素。
  • next():返回下一个元素并移动游标。
  • remove():删除当前游标指向的元素。
  • hasPrevious():判断是否有上一个元素。
  • previous():返回上一个元素并移动游标。
  • add(E e):往集合中添加指定的元素。
  • set(E e):修改集合中指定元素的值。

例如:

复制代码

import java.util.ArrayList;

import java.util.List;

import java.util.ListIterator;

public class ListIteratorTest {

public static void main(String[] args) {

ArrayList<String> list_test = new ArrayList<>();

list_test.add("aaa");

list_test.add("bbb");

list_test.add("ccc");

System.out.println("Before iterate : " + list_test);

ListIterator<String> it = list_test.listIterator();

while (it.hasNext()) {

System.out.println(it.next() + ", " + it.previousIndex() + ", " + it.nextIndex());

}

while (it.hasPrevious()) {

System.out.print(it.previous() + " ");

}

System.out.println();

it = list_test.listIterator(1);

while (it.hasNext()) {

String t = it.next();

System.out.println("it.next:" + t);

if ("ccc".equals(t)) {

it.set("nnn");

} else {

it.add("kkk");

}

}

System.out.println("After iterate : " + list_test);

}

}

3. 使用 subList 方法

  1. 讲解如何使用 List 的 subList 方法截取原列表的一部分,形成一个新的子列表。

List的subList方法可以截取原列表的一部分,形成一个新的子列表。语法如下:

复制代码

public List<E> subList(int fromIndex, int toIndex);

该方法返回包含从索引fromIndex(包括)到索引toIndex(不包括)元素的List集合。

例如:

复制代码

import java.util.ArrayList;

import java.util.List;

public class SubListTest {

public static void main(String[] args) {

List<Integer> list = new ArrayList<>();

for (int i = 0; i < 10; i++) {

list.add(i);

}

List<Integer> subList = list.subList(3, 8);

System.out.println("subList: " + subList);

}

}

在新的子列表中添加或删除元素时,原列表也会发生相应改变。但是如果在原列表中删除或添加元素,调用原列表中的方法没问题,当调用subList方法生成的集合的方法时就会产生异常。

如果要生成一个独立的子列表,可以使用硬复制的方式,而不是直接使用subList方法。例如:

复制代码

import java.util.ArrayList;

public class SubListTest {

public static void main(String[] args) {

ArrayList<String> arrayList = new ArrayList<>();

arrayList.add("a");

arrayList.add("b");

arrayList.add("c");

arrayList.add("d");

ArrayList<String> arrayList_sublist = new ArrayList<>();

for(int i = 1; i <= 2; i++){

arrayList_sublist.add(arrayList.get(i));

}

System.out.println(arrayList_sublist);

}

}

相关推荐
mit6.8243 分钟前
[Redis#6] list | 命令 | 应用 | 消息队列 | 微博 Timeline
linux·redis·后端·云原生·架构
Zfox_6 分钟前
【Linux】线程池设计 + 策略模式
linux·运维·c语言·c++·策略模式
AI原吾7 分钟前
探索Python词云库WordCloud的奥秘
开发语言·python·信息可视化·wordcloud
清酒伴风(面试准备中......)17 分钟前
Redis使用场景-缓存-缓存穿透
java·数据库·redis·缓存·面试·实习
速盾cdn18 分钟前
速盾高防cdn支持移动端独立缓存
开发语言·安全·web安全
懵懵懂懂程序员21 分钟前
Debezium Engine监听binlog实现缓存更新与业务解耦
java·spring boot·redis·缓存
sanqima22 分钟前
解决“磁盘已插上,但Windows系统无法识别“问题
windows·硬盘无法识别
LG.YDX26 分钟前
java:拆箱和装箱,缓存池概念简单介绍
java·开发语言
kirito学长-Java27 分钟前
springboot/ssm大学校园生活信息平台Java校园活动论坛交流问卷系统web源码
java·开发语言·spring