Java集合
在Java中,如果一个Java对象可以再内部持有若干其他Java对象,并对外提供访问接口,我们把这种Java对象称为集合。很显然Java数组可以看做一种集合,那么Java提供了数组可以充当集合,为什么还需要其他集合类?
因为数组有如下限制:
- 数组初始化大小不可变。
- -数组只能按索引顺序存取。
因此,我们需要各种不同类型的集合类来处理不同的数据。例如
- 可变大小的顺序链表
- 保证无重复元素的集合
- …
Java自带库java.util
提供了Collection
集合类,是除Map
外所有其他集合类的根接口。java.util
主要提供了以下三种类型的集合:
- List:有序列表的集合
- Set:无重复元素的集合
- Map:一种
key-value
映射的集合
Java集合设计的特点:
①接口和实现类相分离,如:有序表接口是List,而实现类有ArrayList``LinkedList
等
②支持泛型,可以限制在一个集合中只能放入同一种数据类型的元素
1 | List<String> list = new ArrayList<>(); //只能放入String类型 |
③Java访问集合总是通过统一的方式–迭代器(Iterator)实现,无需知道内部元素按什么方法存储的。
使用List
List是最基础的一种集合,List的行为和数组几乎完全相同。但是直接使用数组在添加和删除元素的时候非常不方便。比如删除数组{"A","b","C","D","E"}
中的索引为2的元素:
删除操作的过程把”C”后面的元素依次往前移一个元素,而添加操作实际上是把指定位置以后的元素都依次向后移一个位置,然后把新增元素放到这个位置,这两操作用数组实现非常麻烦。
而ArrayList
封装了这些方法:比如一个ArrayList有5个元素,实际数组为6,当新增时ArrayList自动移动需要移动的元素,然后往指定位置添加一个元素,size加1。如果数组满没有空位置,ArrayList也会创建一个更大的新数组,复制旧数组的所有元素。
其他方法类似,这种封装让我们专注操作List操作,而不用关心内部如何移动。
List<E>
接口几个主要的方法:
- 在末尾添加一个元素:
void add(E e)
- 在指定位置添加一个元素:
void add(int index, E e)
- 删除指定索引的元素:
int remove(int index)
- 删除某个元素:
int remove(Object e)
- 获取指定索引的元素:
E get(int index)
- 获取链表大小(包含元素的个数):
int size()
LinkedList通过链表也实现了List接口,它的内部每个元素都指向下一个元素
与ArrayList比较
List特点
使用List时,我们要关注List接口的规范。List接口允许我们添加重复的元素,也可以添加Null
创建List
除了使用ArrayList和LinkedList,我们还可以通过List提供的of()方法创建,根据给定元素快算创建List
1 | List<Integer> list = List.of(1, 2, 5); |
遍历List
Iterator
本身是一个对象,是由List实例调用Iterator()
方法创建的。Iterator
对象知道如何遍历一个List
,并且不同的List
类型,返回的Iterator
对象实现也是不同的,但总是具有最高的访问效率。
Iterator
对象有两个方法:boolean hasNext()
判断是否有下一个元素,E next()
返回下一个元素。
1 | List<String> list = List.of("apple", "ipad", "mac"); |
另只要实现了Iterator
接口的集合类都可以直接用for each
循环来遍历。Java编译器会自动把for each
循环变成Iterator
的调用,原因在于Iterator
接口定义了Iterator<E> iterator方法
强迫集合类必须返回一个Iterator
实例。
1 | List<String> list = List.of("apple", "ipad", "mac"); |
List和Array转换
- 调用toArray()方法直接返回一个Object[]数组。缺点:丢失类型信息
- 给toArray(T[])传入一个类型相同的Array,List内部自动把元素复制到传入的Array中
- 使用List接口定义的
T[] toArray(IntFunction<T[]> generator)
方法
1 | Integer[] array = list.toArray(Integer[]::new); |
把Array变成List简单一些,直接List.of(T...)
对于JDK 11之前使用Arrays.asList(T...)
注意:返回的List不一定是ArrayList或者LinkedList。如果调用List.of()
,返回的是一个只读List,对只读List
调用add()
、remove()
方法会抛出UnsupportedOperationException
。
编写equals方法
使用Map
使用List
来实现存在效率非常低的问题,因为平均需要扫描一半的元素才能确定,而Map
这种键值(key-value)映射表的数据结构,作用就是能高效通过key
快速查找value
(元素)。
1 | Student s = new Student("XiaoMing", 99); |
查询key是否存在,调用boolean containsKey(k key)
Map中不存在重复的key,因为放入相同的key,只会把原有的key-value对应的value给替换掉。
遍历Map
- 遍历key,
for each
循环遍历Map实例的keyset()
方法返回的Set集合,包含不重复的Key的集合
1 | Map<String, Integer> map = new HashMap<>(); |
- 同时遍历,
for each
循环遍历Map实例的entrySet()
集合,包含每一个key-value
映射
1 | for (Map.Entry<String, Integer> entry : map.entrySet()){ |