Java 集合 -- List
Java Collection 简介
Java标准库自带的java.util包提供了集合类:Collection,它是除Map外所有其他集合类的根接口。
Java的java.util包主要提供了以下三种类型的集合:
-
List:一种有序列表的集合,例如,按索引排列的Student的List;
-
Set:一种保证没有重复元素的集合,例如,所有无重复名称的Student的Set;
-
Map:一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map。
Java Collection 接口的继承关系如下图:
Java集合的设计有几个特点:
-
一是实现了接口和实现类相分离,例如,有序表的接口是List,具体的实现类有ArrayList,LinkedList等,
-
二是支持泛型,我们可以限制在一个集合中只能放入同一种数据类型的元素,例如:
List<String> list = new ArrayList<>(); // 只能放入String类型
最后,Java访问集合总是通过统一的方式——迭代器(Iterator)来实现,它最明显的好处在于无需知道集合内部元素是按什么方式存储的。
部分遗留的集合类(不建议使用):
-
Hashtable:一种线程安全的Map实现;
-
Vector:一种线程安全的List实现;
-
Stack:基于Vector实现的LIFO的栈。
还有一小部分接口是遗留接口,也不应该继续使用:
- Enumeration
:已被Iterator 取代。
使用 List
在集合类中,List是最基础的一种集合:它是一种有序列表。List的索引和数组一样,从0开始。
List 的实现类 ArrayList
在实际应用中,需要增删元素的有序列表,我们使用最多的是ArrayList。实际上,ArrayList在内部使用了数组来存储所有元素。
ArrayList把添加和删除的操作封装起来,让我们操作List类似于操作数组,却不用关心内部元素如何移动。
List 的实现类 LinkedLsit
LinkedList通过“链表”也实现了List接口。在LinkedList中,它的内部每个元素都指向下一个元素.
比较一下ArrayList和LinkedList:
通常情况下,我们总是优先使用ArrayList。
List 的特点
List接口允许我们添加重复的元素,即List内部的元素可以重复:
public class ListMain {
public static void main(String[] args){
// 实例化一个 ArrayList 列表,命名为 list
// 其中只能存放String 类型元素
List<String> list = new ArrayList<>();
list.add("apple"); // 使用 add()方法添加元素
list.add("pear");
list.add("apple");// 允许重复添加元素
System.out.println(list.size()); // 使用size()方法获取列表长度
System.out.println(list);
}
}
List 允许添加 null:
public class ListMain {
public static void main(String[] args){
// 实例化一个 ArrayList 列表,命名为 list
// 其中只能存放String 类型元素
List<String> list = new ArrayList<>();
list.add("apple"); // 使用 add()方法添加元素
list.add(null);
list.add("apple");// 允许重复添加元素
String second = list.get(1); // 获取列表元素,下标从0开始
System.out.println(second);
System.out.println(list);
}
}
创建 List
除了使用ArrayList和LinkedList,我们还可以通过List接口提供的of()方法,根据给定元素快速创建List:
List<Integer> list = List.of(1, 2, 5);
但是List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常。
注意:只有JDK9及以上,才支持 of()方法快速创建List,JDK8不支持。
遍历 List
可以用for循环根据索引配合get()方法遍历:
public class ListMain {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("apple");
list.add("null");
list.add("apple");
for (int i=0; i<list.size(); i++){
String s = list.get(i);
System.out.println(s);
}
}
}
因为get(int)方法只有ArrayList的实现是高效的,换成LinkedList后,索引越大,访问速度越慢。
所以要始终坚持使用迭代器Iterator来访问List。
Iterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。
Iterator<String> it = list.iterator()
Iterator对象知道如何遍历一个List,并且不同的List类型,返回的Iterator对象实现也是不同的,但总是具有最高的访问效率。
Iterator对象有两个方法:
-
boolean hasNext()判断是否有下一个元素,
-
next()返回下一个元素。
因此,使用Iterator遍历List代码如下:
public class ListMain {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("apple");
list.add("null");
list.add("apple");
Iterator<String> iterator = list.iterator();
for (;iterator.hasNext();){
String s = iterator.next();
System.out.println(s);
}
}
}
记住,通过Iterator遍历List永远是最高效的方式。
并且,由于Iterator遍历是如此常用,所以,Java的增强for循环本身就可以帮我们使用Iterator遍历。
把上面的代码再改写如下:
public class ListMain {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("apple");
list.add("null");
list.add("apple");
// Iterator<String> iterator = list.iterator();
for (String s:list){ // 增强for循环
// String s = iterator.next();
System.out.println(s);
}
}
}
上述代码就是我们编写遍历List的常见代码。
实际上,只要实现了Iterable接口的集合类都可以直接用增强for循环来遍历.
Java编译器本身并不知道如何遍历集合对象,但它会自动把增强for循环变成Iterator的调用,原因就在于Iterable接口定义了一个Iterator
List 与 Array 的转换
array 表示数组。
把List变为Array有三种方法,
-
第一种是调用toArray()方法直接返回一个Object[]数组
-
第二种方式是给toArray(T[])传入一个类型相同的Array,List内部自动把元素复制到传入的Array中
-
最后一种更简洁的写法是通过List接口定义的T[] toArray(IntFunction<T[]> generator)方法
每天学习一点点,每天进步一点点。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
· Ai满嘴顺口溜,想考研?浪费我几个小时
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想