Java语言基础(SE)-第七节 集合(Collections)
集合简介
- 集合(Collections)
- java.util 包中集合框架
Collections Framework
,提供了常用的数据结构:ArrayList、LinkedList、Queue、Stack、HashSet、HashMap
等
- java.util 包中集合框架
-
集合框架预览
- 紫色代表接口,箭头表示某个类实现了某个接口
- 蓝色代表抽象类,不能实例化,从下往上是父类到子类
- 绿色代表类,可以实例化
ArrayList
- 数组的局限性:无法动态扩容、操作元素的过程不够面向对象
- ArrayList 是 Java 中的动态数组:一个可以动态扩容的数组、封装了各种实用的数组操作
- ArrayList 的扩容原理
- ArrayList 的最小容量是 10 (可查看源码)
- 每次扩容时,新容量是旧容量的 1.5 倍
-
ArrayList 的常用方法
private int size; public boolean isEmpty() public boolean contains(Object o) public int indexOf(Object o) public int lastIndexOf(Object o) public E get(int index) public E set(int index, E element) public boolean add(E e) public void add(int index, E element) public E remove(int index) public boolean remove(Object o) public void clear() public boolean addAll(Collection<? extends E> c) public boolean addAll(int index, Collection<? extends E> c public boolean removeAll(Collection<?> c) public boolean retainAll(Collection<?> c) public void forEach(Consumer<? super E> action) public void sort(Comparator<? super E> c) public Object[] toArray() public <T> T[] toArray(T[] a) //根据现有数组的实际大小(现有数组的容量为1w,但是实际存了10个元素,浪费),进行缩小容量,提高性能 public void trimToSize() //指定动态数组一次性扩展到多少容量,不用每次不足再动态计算,提前扩容 public void ensureCapacity(int minCapacity)
-
ArrayList 的基本使用
ArrayList list = new ArrayList(); list.add(11); list.add(false); list.add(null);//可以添加null值 list.add(3.4); list.add(0,"jack"); list.add('8'); //3 System.out.println(list.indexOf(null)); //6 System.out.println(list.size()); //[jack, 11, false, null, 3.4, 8] System.out.println(list);
-
retainAll/removeAll
//以接口作为类型,便于扩展 List<Integer> list1 = new ArrayList<>(); list1.add(11); list1.add(22); list1.add(33); list1.add(44); List<Integer> list2 = new ArrayList<>(); list2.add(22); list2.add(44); //[11, 33] //list1.removeAll(list2); //从list中删掉list2除中元素以外的所有元素 //[22, 44] list1.retainAll(list2); System.out.println(list1);
-
toArray
List<Integer> list = new ArrayList<>(); list.add(11); list.add(22); list.add(33); //将ArrayList转化为普通数组,但是类型是Object的 Object[] array1 = list.toArray(); //[Ljava.lang.Object;@7852e922 System.out.println(array1); //[11, 22, 33] System.out.println(Arrays.toString(array1)); //转化为指定类型的普通数组 Integer[] array2 = list.toArray(new Integer[0]); //[Ljava.lang.Integer;@4e25154f System.out.println(array2); //[11, 22, 33] System.out.println(Arrays.toString(array2));
遍历
-
举例
List<Integer> list = new ArrayList<>(); list.add(11); list.add(22); list.add(33); //方法1 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } //方法2:使用迭代器 //拿到迭代器 Iterator it = list.iterator(); //是否有下一个 while (it.hasNext()) { Object obj = it.next(); System.out.println(obj); } //方法3 for (Object obj : list) { System.out.println(obj); } //方法4 list.forEach((obj)->{ System.out.println(obj); });
-
for-each 格式
for(元素类型 变量名 : 数组/Iterable){}
- 实现了 Iterable 接口的对象,都可以使用 for-each 格式遍历元素
- 比如 List、Set 等
- Iterable 在使用 foreach 格式遍历元素时,本质是使用了 Iterator 对象
- 实现了 Iterable 接口的对象,都可以使用 for-each 格式遍历元素
- 自定义 Iterable、Iterator
-
如何自定义一个集合,可以使用foreach格式遍历元素
package com.zh; import java.util.Iterator; public class ClassRoom implements Iterable<String> { //存放所有学生 private String[] students; public ClassRoom(String... students) { this.students = students; } //实现接口方法Iterable,该方法要求返回一个Iterator(迭代器)接口类型的对象 @Override public Iterator<String> iterator() { //返回一个Iterator类型的对象 return new ClassRoomIterator(); } //创建一个私有类继实现Iterator这个接口 private class ClassRoomIterator implements Iterator<String> { private int index; //该方法用于判断当前数组是否还有下一个元素 @Override public boolean hasNext() { return index < students.length; } //该方法是用来返回当前数组的下一个元素 @Override public String next() { return students[index++]; } } } //main函数 ClassRoom room = new ClassRoom("jack","rose"); //jack rose for (String name : room) { System.out.println(name); } //方法2 Iterator<String> it = room.iterator(); while (it.hasNext()) { String obj = it.next(); System.out.println(obj); }
-
- 遍历的注意点
-
需求:通过遍历的方式,挨个删除所有的元素
List<Integer> list = new ArrayList<>(); list.add(11); list.add(22); list.add(33); list.add(44); int size = list.size(); for (int i = 0; i < size; i++) { //报错:java.lang.IndexOutOfBoundsException //list.remove(i); } for (int i = 0; i < list.size(); i++) { list.remove(i); } //[22, 44] 注意!!!,并不能删除完全 System.out.println(list); for (Integer e : list) { //报错: java.util.ConcurrentModificationException //list.remove(e); } list.forEach((e)->{ //报错:java.util.ConcurrentModificationException list.remove(e); }); Iterator<Integer> it = list.iterator(); while (it.hasNext()) { //报错:java.util.ConcurrentModificationException list.remove(it.next()); }
- 如果希望在遍历元素的同时删除元素
- 请使用 Iterator 进行遍历
- 然后使用 Iterator 的 remove 方法删除元素
Iterator<Integer> it = list.iterator(); while (it.hasNext()) { it.next(); it.remove(); } //[],删除成功 System.out.println(list);
- 使用迭代器、forEach 遍历集合元素时,若使用了集合自带的方法修改集合的长度(比如 add、remove 等方法)
- 那么会抛出 java.util.ConcurrentModificationException 异常
-
ListIterator
-
ListIterator 继承自 Iterator,在 Iterator 的基础上增加了一些功能
boolean hasNext(); E next(); boolean hasPrevious(); E previous(); int nextIndex(); int previousIndex(); void remove(); void set(E e); void add(E e);
-
举例使用
ListIterator<Integer> it = list.listIterator(); while (it.hasNext()) { //11 22 33 System.out.println(it.next()); } while (it.hasPrevious()) { //33 22 11 System.out.println(it.previous()); } ListIterator<Integer> it = list.listIterator(); while (it.hasNext()) { it.set(it.next()+55); } //[66, 77, 88] System.out.println(list); while (it.hasNext()) { it.add(66); //11 22 33 System.out.println(it.next()); it.add(77); } //[66, 11, 77, 66, 22, 77, 66, 33, 77] System.out.println(list);
LinkedList
- LinkedList 是一个双向链表
- 实现了 List 接口
- API 跟 ArrayList 类似
- LinkedList vs ArrayList
- ArrayList :开辟、销毁内存空间的次数相对较少,但可能造成内存空间浪费(可以通过缩容解决)
- LinkedList :开辟、销毁内存空间的次数相对较多,但不会造成内存空间的浪费
- 如果频繁在尾部进行添加、删除操作, ArrayList、LinkedList 均可选择
- 如果频繁在头部进行添加、删除操作,建议选择使用 LinkedList
- 如果有频繁的(在任意位置)添加、删除操作,建议选择使用 LinkedList
- 如果有频繁的查询操作(随机访问操作),建议选择使用 ArrayList
Stack
- Stack,译为“栈”,只能在一端进行操作
- 往栈中添加元素的操作,一般叫做 push,入栈
- 从栈中移除元素的操作,一般叫做 pop,出栈(只能移除栈顶元素,也叫做:弹出栈顶元素)
- 后进先出的原则,Last In First Out,LIFO
- 注意: 这里说的“栈”与内存中的“栈空间”是两个不同的概念
-
常用方法
public E push(E item) public E pop() public E peek() //返回栈顶元素 public int size() public boolean empty() public int search(Object o)
-
举例使用:
Stack<Integer> stack = new Stack(); stack.push(11); stack.push(22); stack.push(33); //33 System.out.println(stack.peek()); //3 System.out.println(stack.search(11)); //33 22 11 while (!stack.empty()) { System.out.println(stack.pop()); }
Queue
- Queue,译为“队列” ,只能在头尾两端进行操作
- 队尾(rear):只能从队尾添加元素,一般叫做 入队
- 队头(front):只能从队头移除元素,一般叫做 出队
- 先进先出的原则,First In First Out,FIFO
- 常用方法
- java.util.Queue 是一个接口,它的常用实现是 LinkedList
boolean add(E e); //入队 boolean offer(E e);//入队 E remove();//出队 E poll();//出队 E element();//返回队头 E peek();//返回队头 boolean isEmpty() public int size()
-
举例:
Queue<Integer> queue = new LinkedList<>(); queue.add(11); queue.add(22); queue.add(33); //11 System.out.println(queue.element()); while (!queue.isEmpty()) { //11 22 33 System.out.println(queue.remove()); }
Set
HashSet
- 跟ArrayList一样,但是也有区别:
- 元素是无序的,不能通过索引来访问元素
- 不能有重复的元素,可以去重复
Set<String> set = new HashSet<>(); set.add("Jack"); set.add("Rose"); set.add("Jim"); set.add("Jack"); set.add("Kate"); //4 System.out.println(set.size()); //[Kate, Rose, Jack, Jim] System.out.println(set); set.remove("Rose"); //[Kate, Jack, Jim] System.out.println(set);
LinkedHashSet
- LinkedHashSet 在 HashSet 的基础上,记录了元素的添加顺序
- 就是添加的顺序是什么,在容器里面的元素顺序就是什么
TreeSet
-
TreeSet 要求元素必须具备可比较性,默认按照从小到大的顺序遍历元素
Set<String> set = new TreeSet<>(); set.add("Jack"); set.add("Rose"); set.add("Jim"); set.add("Kate"); set.add("Rose"); set.add("Larry"); //[Jack, Jim, Kate, Larry, Rose] System.out.println(set); for (String str : set) { System.out.println(str); }//Jack Jim Kate Larry Rose
-
自定义比较方式
//自定义比较方式 Set<Integer> set = new TreeSet<>((i1,i2) -> i2 - i1); set.add(33); set.add(11); set.add(55); set.add(22); set.add(44); //[55, 44, 33, 22, 11] System.out.println(set); for (Integer i : set) { System.out.println(i); }//55 44 33 22 11
Map
HashMap
- HashMap 存储的是键值对(key-value),Map 译为“映射”,有些编程语言中叫做“字典”
Map<String,Integer> map = new HashMap<>();
map.put("Jack", 11);
map.put("Rose", 22);
map.put("Jim", 22);
map.put("Jack", 33);
map.put("Kate", 11);
//4
System.out.println(map.size());
//33
System.out.println(map.get("Jack"));
//{Kate=11, Rose=22, Jack=33, Jim=22}
System.out.println(map);
map.remove("Rose");
//{Kate=11, Jack=33, Jim=22}
System.out.println(map);
//遍历
Set<String> keys = map.keySet();
for (String key : keys) {
//先遍历key,又通过key去找到value(整体效率低)
System.out.println(key + "=" + map.get(key));
}//Kate=11 Jack=33 Jim=22
//只能遍历value
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.println(value);
}//11 33 22
//推荐
//一个Entry对象代表一个键值对
Set<Entry<String,Integer>> entries =map.entrySet();
for (Entry<String, Integer> entry : entries) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}//Kate=11 Jack=33 Jim=22
//推荐
map.forEach((key,value)->{
System.out.println(key + "=" + value);
});//Kate=11 Jack=33 Jim=22
LinkedHashMap
- LinkedHashMap 在 HashMap 的基础上,记录了元素的添加顺序
Map<String,Integer> map = new LinkedHashMap<>();
map.put("Jack", 11);
map.put("Rose", 22);
map.put("Jim", 33);
map.put("Kate", 44);
//{Jack=11, Rose=22, Jim=33, Kate=44}
System.out.println(map);
map.forEach((k,v)->{
System.out.println(k + "=" + v);
});//Jack=11 Rose=22 Jim=33 Kate=44
map.remove("Rose");
//{Jack=11, Jim=33, Kate=44}
System.out.println(map);
TreeMap
- TreeMap 要求 key 必须具备可比较性,默认按照从小到大的顺序遍历 key
Map<String,Integer> map = new TreeMap<>();
map.put("Jack", 11);
map.put("Rose", 22);
map.put("Jim", 33);
map.put("Kate", 44);
//{Jack=11, Jim=33, Kate=44, Rose=22}
System.out.println(map);
map.forEach((k,v)->{
System.out.println(k + "=" + v);
});//Jack=11 Jim=33 Kate=44 Rose=22
List vs Set vs Map
- List 的特点
- 可以存储重复的元素
- 可以通过索引访问元素
- Set 的特点
- 不可以存储重复的元素
- 不可以通过索引访问元素
- Map 的特点
- 不可以存储重复的 key,可以存储重复的 value
- 不可以通过索引访问 key-value
- Set 的底层是基于 Map 实现的
- HashSet 底层用了 HashMap
- LinkedHashSet 底层用了 LinkedHashMap
- TreeSet 底层用了TreeMap
java.util.Collections
- java.util.Collections 是一个常用的集合工具类,提供了很多实用的静态方法
- 类似于Arrays,Arrays是针对数组的工具类
- JDK中凡是复数(Arrays)形式的类型,基本都是工具类
List<Integer> list = new ArrayList<>();
//将元素添加到list中
Collections.addAll(list, 45,11,22);