第 12 章 集合
12.1 集合的理解和好处
前面我们保存多个数据使用的是数组,那么数组有不足的地方,我们分析一下
12.1.1 数组
- 长度开始时必须指定,而且一旦指定,不能更改
- 保存的必须为同一类型的元素
- 使用数组进行增加/删除元素的示意代码 – 比较麻烦
写出Person数组扩容示意代码。- Person[] pers = new Person[1]; //大小是1
- pers[0]=new Person();
- //增加新的Person对象?
- Person[] pers2 = new Person[pers.length+1]; //新创建数组
- for(int i = 0; i < pers.length; i++){ //拷贝pers数组的元素到pers2
- pers2[i] = pers[i];
- }
- pers2[pers2.length-1] = new Person();//添加新的对象
复制代码 12.1.2 集合
- 可以动态保存任意多个对象,使用比较方便!
- 提供了一系列方便的操作对象的方法:add、remove、set、get等
- 使用集合添加,删除新元素的示意代码- 简洁了
12.2 集合的框架体系
Java 的集合类很多,主要分为两大类,如图:
<img alt="image-20250820224750255" > <img alt="image-20250820224830028" > - public class Collection_ {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- //解读
- //1. 集合主要是两组(单列集合 , 双列集合)
- //2. Collection 接口有两个重要的子接口 List Set , 他们的实现子类都是单列集合
- //3. Map 接口的实现子类 是双列集合,存放的 K-V
- //4. 把老师梳理的两张图记住
- //Collection
- //Map
- ArrayList arrayList = new ArrayList();
- arrayList.add("jack");
- arrayList.add("tom");
- HashMap hashMap = new HashMap();
- hashMap.put("NO1", "北京");
- hashMap.put("NO2", "上海");
- }
- }
复制代码 12.3 Collection 接口和常用方法
12.3.1 Collection 接口实现类的特点
- public interface Collection<E> extends Iterable<E>
复制代码
- collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的
Collection 接口常用方法,以实现子类 ArrayList 来演示. CollectionMethod.java
- public class CollectionMethod {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- List list = new ArrayList();
- // add:添加单个元素
- list.add("jack");
- list.add(10);//list.add(new Integer(10))
- list.add(true);
- System.out.println("list=" + list);
- // remove:删除指定元素
- //list.remove(0);//删除第一个元素
- list.remove(true);//指定删除某个元素
- System.out.println("list=" + list);
- // contains:查找元素是否存在
- System.out.println(list.contains("jack"));//T
- // size:获取元素个数
- System.out.println(list.size());//2
- // isEmpty:判断是否为空
- System.out.println(list.isEmpty());//F
- // clear:清空
- list.clear();
- System.out.println("list=" + list);
- // addAll:添加多个元素
- ArrayList list2 = new ArrayList();
- list2.add("红楼梦");
- list2.add("三国演义");
- list.addAll(list2);
- System.out.println("list=" + list);
- // containsAll:查找多个元素是否都存在
- System.out.println(list.containsAll(list2));//T
- // removeAll:删除多个元素
- list.add("聊斋");
- list.removeAll(list2);
- System.out.println("list=" + list);//[聊斋]
- // 说明:以ArrayList实现类来演示.
- }
- }
复制代码 12.3.2 Collection 接口遍历元素方式 1-使用 Iterator(迭代器)
基本介绍- java.util
- 接口 Iterator<E>
- 所有已知子接口:
- ListIterator<E>, XMLStreamReader
- 所有已知实现类:
- BeanContextSupport.BCSIterator, EventReaderDelegate, Scanner
复制代码
- Iterator对象称为迭代器,主要用于遍历 Collection 集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
- Iterator 的结构.[看一张图]
- Iterator 仅用于遍历集合,Iterator 本身并不存放对象。
<img alt="image-20250820230941451" >
在调用 iterator.next() 方法之前必须要调用 iterator.hasNext() 进行检测。若不调用,且下一条记录无效,直接调用 it.next() 会抛出 NoSuchElementException 异常。 迭代器的使用案例
看案例演示 CollectionIterator.java- public class CollectionIterator {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- Collection col = new ArrayList();
- col.add(new Book("三国演义", "罗贯中", 10.1));
- col.add(new Book("小李飞刀", "古龙", 5.1));
- col.add(new Book("红楼梦", "曹雪芹", 34.6));
- //System.out.println("col=" + col);
- //现在希望能够遍历 col集合
- //1. 先得到 col 对应的 迭代器
- Iterator iterator = col.iterator();
- //2. 使用while循环遍历
- // while (iterator.hasNext()) {//判断是否还有数据
- // //返回下一个元素,类型是Object
- // Object obj = iterator.next();
- // System.out.println("obj=" + obj);
- // }
- //一个快捷键,快速生成 while => itit
- //显示所有的快捷键的的快捷键 ctrl + j
- while (iterator.hasNext()) {
- Object obj = iterator.next();
- System.out.println("obj=" + obj);
- }
- //3. 当退出while循环后 , 这时iterator迭代器,指向最后的元素
- // iterator.next();//NoSuchElementException
- //4. 如果希望再次遍历,需要重置我们的迭代器
- iterator = col.iterator();
- System.out.println("===第二次遍历===");
- while (iterator.hasNext()) {
- Object obj = iterator.next();
- System.out.println("obj=" + obj);
- }
- }
- }
- class Book {
- private String name;
- private String author;
- private double price;
- public Book(String name, String author, double price) {
- this.name = name;
- this.author = author;
- this.price = price;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getAuthor() {
- return author;
- }
- public void setAuthor(String author) {
- this.author = author;
- }
- public double getPrice() {
- return price;
- }
- public void setPrice(double price) {
- this.price = price;
- }
- @Override
- public String toString() {
- return "Book{" +
- "name='" + name + '\'' +
- ", author='" + author + '\'' +
- ", price=" + price +
- '}';
- }
- }
复制代码 12.3.3 Collection 接口遍历对象方式 2 - for 循环增强
增强 for 循环,可以代替 iterator 迭代器,特点:增强 for 就是简化版的 iterator,本质一样。只能用于遍历集合或数组。
基本语法- for(元素类型 元素名 : 集合名或数组名) {
- 访问元素
- }
复制代码 案例演示
CollectionFor.java- for (Object object : col) {
- System.out.println(object);
- }
复制代码- public class CollectionFor {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- Collection col = new ArrayList();
- col.add(new Book("三国演义", "罗贯中", 10.1));
- col.add(new Book("小李飞刀", "古龙", 5.1));
- col.add(new Book("红楼梦", "曹雪芹", 34.6));
- //解读
- //1. 使用增强for, 在Collection集合
- //2. 增强for, 底层仍然是迭代器
- //3. 增强for可以理解成就是简化版本的 迭代器遍历
- //4. 快捷键方式 I
- // for (Object book : col) {
- // System.out.println("book=" + book);
- // }
- for (Object o : col) {
- System.out.println("book=" + o);
- }
- //增强for,也可以直接在数组使用
- // int[] nums = {1, 8, 10, 90};
- // for (int i : nums) {
- // System.out.println("i=" + i);
- // }
- }
- }
复制代码 12.4 List 接口和常用方法
12.4.1 List 接口基本介绍
List 接口是 Collection 接口的子接口 List.java
- List 集合类中元素有序(即添加顺序和取出顺序一致)、且可重复 [案例]
- List 集合中的每个元素都有其对应的顺序索引,即支持索引。 [案例]
- List 容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API 中 List 接口的实现类有:
- List list = new ArrayList();
- list.add("tom");
- list.add("jack");
- list.add("mary");
- list.add("smith");
- list.add("smith2");
- list.add("kristina");
- System.out.println(list);
- // 3的案例
- System.out.println(list.get(4));//smith2
复制代码 常用的有:ArrayList、LinkedList 和 Vector。
- public class List_ {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- //1. List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复 [案例]
- List list = new ArrayList();
- list.add("jack");
- list.add("tom");
- list.add("mary");
- list.add("hsp");
- list.add("tom");
- System.out.println("list=" + list);
- //2. List集合中的每个元素都有其对应的顺序索引,即支持索引
- // 索引是从0开始的
- System.out.println(list.get(3));//hsp
- //3.
- }
- }
复制代码 12.4.2 List接口的常用方法
- public class ListMethod {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- List list = new ArrayList();
- list.add("张三丰");
- list.add("贾宝玉");
- // void add(int index, Object ele):在index位置插入ele元素
- //在index = 1的位置插入一个对象
- list.add(1, "拉里");
- System.out.println("list=" + list);
- // boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
- List list2 = new ArrayList();
- list2.add("jack");
- list2.add("tom");
- list.addAll(1, list2);
- System.out.println("list=" + list);
- // Object get(int index):获取指定index位置的元素
- //说过
- // int indexOf(Object obj):返回obj在集合中首次出现的位置
- System.out.println(list.indexOf("tom"));//2
- // int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
- list.add("拉里");
- System.out.println("list=" + list);
- System.out.println(list.lastIndexOf("拉里"));
- // Object remove(int index):移除指定index位置的元素,并返回此元素
- list.remove(0);
- System.out.println("list=" + list);
- // Object set(int index, Object ele):设置指定index位置的元素为ele , 相当于是替换.
- list.set(1, "玛丽");
- System.out.println("list=" + list);
- // List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
- // 注意返回的子集合 fromIndex <= subList < toIndex
- List returnlist = list.subList(0, 2);
- System.out.println("returnlist=" + returnlist);
- }
- }
复制代码 在开发中,需要线程同步安全时,考虑使用 Vector- public class ListExercise {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- /*
- 添加10个以上的元素(比如String "hello" ),在2号位插入一个元素"教育",
- 获得第5个元素,删除第6个元素,修改第7个元素,在使用迭代器遍历集合,
- 要求:使用List的实现类ArrayList完成。
- */
- List list = new ArrayList();
- for (int i = 0; i < 12; i++) {
- list.add("hello" + i);
- }
- System.out.println("list=" + list);
- //在2号位插入一个元素"教育"
- list.add(1, "教育");
- System.out.println("list=" + list);
- //获得第5个元素
- System.out.println("第五个元素=" + list.get(4));
- //删除第6个元素
- list.remove(5);
- System.out.println("list=" + list);
- //修改第7个元素
- list.set(6, "三国演义");
- System.out.println("list=" + list);
- //在使用迭代器遍历集合
- Iterator iterator = list.iterator();
- while (iterator.hasNext()) {
- Object obj = iterator.next();
- System.out.println("obj=" + obj);
- }
- }
- }
复制代码 </ol>12.6.2 Vector 和 ArrayList 的比较
底层结构版本线程安全(同步)效率扩容倍数ArrayList可变数组jdk1.2不安全,效率高如果有参构造1.5倍
如果是无参
1.第一次10
2.从第二次开始1.5扩Vector可变数组 Object[]jdk1.0安全,效率不高如果是无参,默认10
,满后,就按2倍扩容
如果指定大小,则每次直接按2倍扩容。12.7 LinkedList 底层结构
12.7.1 LinkedList 的全面说明
- LinkedList 底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括 null
- 线程不安全,没有实现同步
12.7.2 LinkedList 的底层操作机制
- public class ListFor {
- @SuppressWarnings({"all"})
- public static void main(String[] args) {
- //List 接口的实现子类 Vector LinkedList
- //List list = new ArrayList();
- //List list = new Vector();
- List list = new LinkedList();
- list.add("jack");
- list.add("tom");
- list.add("鱼香肉丝");
- list.add("北京烤鸭子");
- //遍历
- //1. 迭代器
- Iterator iterator = list.iterator();
- while (iterator.hasNext()) {
- Object obj = iterator.next();
- System.out.println(obj);
- }
- System.out.println("=====增强for=====");
- //2. 增强for
- for (Object o : list) {
- System.out.println("o=" + o);
- }
- System.out.println("=====普通for====");
- //3. 使用普通for
- for (int i = 0; i < list.size(); i++) {
- System.out.println("对象=" + list.get(i));
- }
- }
- }
复制代码 12.7.3 LinkedList的增删改查案例
- transient Object[] elementData; // transient 表示瞬间,短暂的,表示该属性不会被序列号
复制代码 12.8 ArrayList 和 LinkedList 比较
12.8.1 ArrayList 和 LinkedList 的比较
底层结构增删的效率改查的效率ArrayList可变数组较低
数组扩容较高LinkedList双向链表较高,通过链表追加较低如何选择 ArrayList 和 LinkedList:
- 如果改查操作多,选择 ArrayList
- 如果增删操作多,选择 LinkedList
- 一般程序中 80%-90% 是查询,因此大部分情况选 ArrayList
- 项目中灵活根据业务选,可能一个模块用 ArrayList,另一个用 LinkedList
12.9 Set 接口和常用方法
12.9.1 Set 接口基本介绍
- 无序(添加和取出顺序不一致),没有索引 [后面演示]
- 不允许重复元素,最多包含一个 null
- JDK API 中 Set 接口的实现类有:
- public class Vector<E>
- extends AbstractList<E>
- implements List<E>, RandomAccess, Cloneable, Serializable
复制代码 12.9.2 Set 接口的常用方法
和 List 接口一样,Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一样
12.9.3 Set 接口的遍历方式
同 Collection 的遍历方式一样,因为 Set 接口是 Collection 接口的子接口。
- 可以使用迭代器
- 增强 for
- 不能使用索引的方式来获取
12.9.4 Set 接口的常用方法举例
- protected Object[] elementData;
复制代码 12.10.3 HashSet 底层机制说明
分析HashSet底层是HashMap, HashMap底层是(数组+链表+红黑树)
- public synchronized E get(int index) {
- if (index >= elementCount)
- throw new ArrayIndexOutOfBoundsException(index);
- return elementData(index);
- }
复制代码
[code]public class HashSetIncrement { public static void main(String[] args) { /* HashSet底层是HashMap, 第一次添加时,table 数组扩容到 16, 临界值(threshold)是 16*加载因子(loadFactor)是0.75 = 12 如果table 数组使用到了临界值 12,就会扩容到 16 * 2 = 32, 新的临界值就是 32*0.75 = 24, 依次类推 */ HashSet hashSet = new HashSet();// for(int i = 1; i = MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树), 否则仍然采用数组扩容机制 */// for(int i = 1; i Node -> 加入table , 就算是增加了一个size++ */ for(int i = 1; i |