第 12 章 集合
第 12 章 集合12.1 集合的理解和好处
前面我们保存多个数据使用的是数组,那么数组有不足的地方,我们分析一下
12.1.1 数组
[*]长度开始时必须指定,而且一旦指定,不能更改
[*]保存的必须为同一类型的元素
[*]使用数组进行增加/删除元素的示意代码 – 比较麻烦
写出Person数组扩容示意代码。
Person[] pers = new Person; //大小是1
pers=new Person();
//增加新的Person对象?
Person[] pers2 = new Person; //新创建数组
for(int i = 0; i < pers.length; i++){ //拷贝pers数组的元素到pers2
pers2 = pers;
}
pers2 = 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);
}
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; iNode -> 加入table , 就算是增加了一个size++ */ for(int i = 1; i
页:
[1]