b1cat`s

Java集合

Word count: 1.5kReading time: 5 min
2020/02/15

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接口,它的内部每个元素都指向下一个元素

image-20200215152120080

与ArrayList比较

image-20200215152141961

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
2
3
4
5
List<String> list = List.of("apple", "ipad", "mac");
for (Iterator<String> it = list.iterator(); it.hashNext();){
String s = it.next();
System.out.println(s);
}

另只要实现了Iterator接口的集合类都可以直接用for each循环来遍历。Java编译器会自动把for each循环变成Iterator的调用,原因在于Iterator接口定义了Iterator<E> iterator方法强迫集合类必须返回一个Iterator实例。

1
2
3
4
List<String> list = List.of("apple", "ipad", "mac");
for (String s : list){
System.out.println(s);
}

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
2
3
4
5
Student s = new Student("XiaoMing", 99);
Map<String, Student> map = new HashMap<>();
map.put("XiaoMing", s); //将小明和Student实例映射关联
Student target = map.get("XiaoMing");//获取小明关联的实例,获取成绩target.score字段即可
//map.get不存在返回null

查询key是否存在,调用boolean containsKey(k key)

Map中不存在重复的key,因为放入相同的key,只会把原有的key-value对应的value给替换掉。

遍历Map

  • 遍历key,for each 循环遍历Map实例的keyset()方法返回的Set集合,包含不重复的Key的集合
1
2
3
4
5
6
7
Map<String, Integer> map = new HashMap<>();
map.put("aplle", 123);
map.put("ipad", 666);
for (String key : map.keyset()){
Integer value = map.get(key);
System.out.println(key + " = " + value);
}
  • 同时遍历,for each 循环遍历Map实例的entrySet()集合,包含每一个key-value映射
1
2
3
4
for (Map.Entry<String, Integer> entry : map.entrySet()){
String key = entry.getKey();
Integer value = entry.getValue();
}
CATALOG
  1. 1. Java集合
    1. 1.1. 使用List
      1. 1.1.0.1. List特点
      2. 1.1.0.2. 创建List
      3. 1.1.0.3. 遍历List
      4. 1.1.0.4. List和Array转换
  2. 1.2. 编写equals方法
  3. 1.3. 使用Map
    1. 1.3.0.1. 遍历Map