Java 基础 - 集合框架
集合概述
集合的由来
通常,我们的程序需要根据程序运行时才知道创建多少个对象。但若非程序运行,程序开发阶段,我们根本不知道到底需要多少个数量的对象,甚至不知道它的准确类型。为了满足这些常规的编程需要,我们要求能在任何时候,任何地点创建任意数量的对象,而这些对象用什么来容纳呢?我们首先想到了数组,但是数组只能放统一类型的数据,而且其长度是固定的,那怎么办呢?集合便应运而生了!
集合是什么?
Java集合类存放于 java.util 包中,是一个用来存放对象的容器。
注意:
① 集合只能存放对象。比如你存一个int 型数据放入集合中,其实它是自动转换成 Integer 类后存入的,Java中每一种基本类型都有对应的引用类型。
② 集合存放的是多个对象的引用,对象本身还是放在堆内存中。
③ 集合可以存放不同类型,不限数量的数据类型。
集合和数组的区别
- 数组存放的类型只能是基本数据类型或引用数据类型,集合中存放的数据类型只能是引用数据类型 。
- 数组是静态的,一个数组实例具有固定的大小,一旦创建了不能改变容量。而集合是可以动态扩展容量,可以根据需要动态改变大小。
- 初始化数组时需要声明存放在数组中的数据类型,而集合可以声明或不声明,当不声明时默认为Object类型,Object是Java中的超类,这就意味着一个集合中可以存放混合类型的数据(既存放String类型的数据又存放Integer类型的数据)
集合框架的基础接口
- Collection:是集合框架的基础接口之一,在Java中不提供该接口的任何直接实现。
- List:是一个可以包含重复元素的集合。每个元素都有对应的顺序索引,实现类主要有ArrayList、LinkedList等。
- Set:是一个不能包含重复元素的集合,并且元素没有特定顺序。
- Map:是用于保存具有映射关系的键值对数据,提供key(键)到value(值)的映射。一个Map不能包含重复的key,每个key最多映射到一个value。
- Queue:是数据结构中队列结构的实现。在queue中元素具有隐含的顺序,且每个queue都有一个head元素。
- Iterable:是一个迭代器接口,使用该接口可以从集合中每次返回其中的一个元素。
集合框架常用集合类
[图片上传失败...(image-7e819-1631774982060)]
Collection接口
Collection接口是Set,Queue,List的父接口。
Collection接口中定义了多种方法可供其子类进行实现,以实现数据操作。
下面是Collection的类结构图:
[图片上传失败...(image-255600-1631774982060)]
Collections工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类。Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。
Collections提供了多个synchronizedXx()方法,把线程不安全的list/Collection/map转换为线程安全0。
public class CollectionsTest {
@Test
public void test1() {
List<Integer> list = new ArrayList<>();
list.add(41);
list.add(13);
list.add(55);
list.add(55);
//1) reverse(list)方法 反转list中的元素
Collections.reverse(list); // 对list做修改 没有返回值
//2) shuffle(list)方法 对list进行随机打乱排列
Collections.shuffle(list);
//3) sort(list) 对list进行排序 需要实现compareTo方法
Collections.sort(list);
//4) swap(list,i,j) 对list中的i处的元素和j处的进行交换
Collections.swap(list,0,3);
//5) max(Collection) 返回Collection中最大的元素(基于compareTo方法)
//6) min(Collection) 返回Collection中最小的元素(基于compareTo方法)
//7) frequency(Collection,i) 返回Collection中i出现的次数
int frequency = Collections.frequency(list, 55);
//8) copy(list dest,list src) 把src的内容复制到dest中
// 应该先新建一个带src.size()个数null的一个list
List<Object> dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest,list);
// 9) 利用synchronizedList(list)方法 将list转换为线程安全的
List<Integer> list1 = Collections.synchronizedList(list);
}
}
Collection和Collections的区别
- java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
- Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
List接口
实现List接口的集合是有序的、可重复的。
List接口主要有四个实现类: 分别是ArrayList、Vector、LinkedList和CopyOnWriteArrayList
常用方法
1.操作元素
- get(int index):返回List集合中指定位置的元素。
- set(int index, Object element):用指定元素替换List集合中指定位置的元素。
- add(Object element):在List集合的尾部添加指定的元素。该方法是从Collection集合继承过来的。
- add(int index, Object element):在List集合的指定位置插入指定元素。
- remove(int index):移除List集合中指定位置的元素。
- remove(Object element):如果List集合中存在指定元素,则从List集合中移除第一次出现的指定元素。该方法是从Collection集合继承过来的。
- clear():从List集合中移除所有元素。该方法是从Collection集合继承过来的。
2.判断元素
- isEmpty():判断List集合中是否有元素,没有返回true,有返回false。该方法是从Collection继承过来的。
- contains(Object element):判断List集合中是否包含指定元素,包含返回true,不包含返回false。该方法是从Collection集合继承过来的。
3.查询元素
- indexOf(Object o):从前往后查找List集合元素,返回第一次出现指定元素的索引,如果此列表不包含该元素,则返回-1。
- lastIndexOf(Object o):从后往前查找List集合元素,返回第一次出现指定元素的索引,如果此列表不包含该元素,则返回-1。
4.其它
- iterator():返回迭代器(Iterator)对象,迭代器对象用于遍历集合。该方法是从Collection继承过来的。
- size():返回List集合中的元素数,返回值是int类型。该方法是从Collection集合继承过来的。
- subList(int fromIndex, int toIndex):返回List集合中指定的 fromIndex(包括 )和 toIndex(不包括)之间的元素集合,返回值为List集合。
遍历集合
集合最常用的操作之一是遍历,遍历就是将集合中的每一个元素取出来,进行操作或计算。
- 使用for循环遍历:List集合可以使用for循环进行遍历,for循环中有循环变量,通过循环变量可以访问List集合中的元素。
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
- 使用for-each循环遍历:for-each循环是针对遍历各种类型集合而推出的,笔者推荐使用这种遍历方法。
for (Object o : list) {
System.out.println(o);
}
- 使用迭代器遍历:Java提供了多种迭代器,List集合可以使用Iterator和ListIterator迭代器。
//获取迭代器
Iterator iterator = list.iterator();
//判断是否还有元素
while(iterator.hasNext()){
System.out.println(iterator.next());
}
注:使用for-each循环遍历和使用迭代器遍历从集合中取出的元素都是Object类型。
ListIterator和Iterator的区别
- Iterator可以遍历Set 和 List 集合,而ListIterator只能用于遍历List。
- Iterator和ListIterator都可实现删除元素,但是ListIterator可以实现遍历时对元素的修改,用set()方法实现。Iterator仅能遍历,不能修改。
- Iterator只能单向遍历 [ hasNext()、next()方法)] ,而ListIterator可以双向遍历(向前/后遍历) [ 从后往前 hasPrevious()、previous()方法 ] 。
- ListIterator接口继承于Iterator接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
Set接口
常用方法
1.操作元素
- add(Object element):在Set集合的尾部添加指定的元素。该方法是从Collection集合继承过来的。
- remove(Object element):如果Set集合中存在指定元素,该方法是从Collection集合继承过来的。
- clear():从Set集合中移除所有元素。该方法是从Collection集合继承过来的。
2.判断元素
- isEmpty():判断Set集合中是否有元素,没有返回true,有返回false。该方法是从Collection集合继承过来的
- contains(Object element):判断Set集合中是否包含指定元素,包含返回true,不包含返回false。该方法是从Collection集合继承过来的。
3.其他
- iterator():返回迭代器(Iterator)对象,迭代器对象用于遍历集合。该方法是从Collection集合继承过来的。
- size():返回Set集合中的元素数,返回值是int类型。该方法是从Collection集合继承过来的。
Set和List的区别
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>
- List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector>
Map接口
Map(映射)集合表示一种非常复杂的集合,允许按照某个键来访问元素。Map集合是由两个集合构成的,一个是键(key)集合,一个是值(value)集合。键集合是Set类型,因此不能有重复的元素。而值集合是Collection类型,可以有重复的元素。Map集合中的键和值是成对出现的。
常用方法
1.操作元素
- get(Object key):返回指定键所对应的值;如果Map集合中不包含该键值对,则返回null。
- put(Object key, Object value):指定键值对添加到集合中。
- remove(Object key):移除键值对。
- clear():移除Map集合中所有键值对。
2.判断元素
- isEmpty():判断Map集合中是否有键值对,没有返回true,有返回false。
- containsKey(Object key):判断键集合中是否包含指定元素,包含返回true,不包含返回false。
- containsValue(Object value):判断值集合中是否包含指定元素,包含返回true,不包含返回false。
3.查看集合
- keySet():返回Map中的所有键集合,返回值是Set类型。
- values():返回Map中的所有值集合,返回值是Collection类型。
- size():返回Map集合中键值对数。
Map与Set、List的关系
1)Map集合与Set集合的关系:如果把Map里的所有key放在一起看,他们就组成了一个Set集合(所有的key没有顺序,key与key之间不能重复),实际上Map确实包含了一个keySet()方法,用户返回Map里所有key组成的Set集合。
2)Map集合与List集合的关系:如果把Map里的所有value放在一起来看,它们又非常类似于一个List:元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map中索引不再使用整数值,而是以另外一个对象作为索引。
Queue
Queue用于模拟队列这种数据结构。队列通常是指“先进先出(FIFO)”的容器。队列的头部保存在队列中存放时间最长的元素,尾部保存存放时间最短的元素。新元素插入到队列的尾部,取出元素会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。
PriorityQueue
优先队列采用堆这种数据结构,元素可以按照任意顺序插, 但是删除的时候总是会删除最小的那个。优先队列主要用于任务调度,当启动新任务时会把优先级最高的任务从队列中删除(习惯上优先级最高的是1)。这里添加的对象(元素)需要实现compareTo方法。
public class PriorityQueueTest {
@Test
public void test1() {
var pq = new PriorityQueue<LocalDate>();
pq.add(LocalDate.of(2021, 7, 1));
pq.add(LocalDate.of(2021, 8, 18));
pq.add(LocalDate.of(2021, 9, 20));
// 调用remove时会把元素中最小的那个删除掉
pq.remove();
System.out.println(pq); //output:[2021-07-01, 2021-09-20]
}
}