集合
集合的体系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-68bQgx1U-1669232941562)(F:\BigData\java\图片\day09图片\集合体系图.png)]
简介:
概述:
集合是用来存储多个同类型元素的容器, 它的长度是 可变的
体系:
-
单列集合:顶层接口是:Collection
-
List体系:
-
特点:有序(元素的存 取 顺序一致),可重复,元素有索引。
-
常用子类:
-
ArrayList:底层数据结构采用的是数组,所以查询(修改)相对较快,增删相对较慢,线程不安全,效率相对较高。
-
LinkedList:底层数据结构采用的是链表,所以查询(修改)相对较慢,增删相对较块,线程不安全,效率相对较高。
-
-
-
Set体系:
- 特点:无序(元素的存 取 顺序不一致),唯一,元素无索引(解释:其实底层是有索引的,但是我们不能用,所以我们理解为无索引)。
- 常用子类:
- HashSet:底层数据结构采用的是哈希表(数组 + 链表),增删改查都快,线程不安全,效率相对较高。
- TreeSet:底层数据结构采用二叉树,可以对元素进行排序,但是这个我们不讲,因为实际开发中的数据从SQL中提取出来的时候就是排好序的。
- 常用子类:
- 特点:无序(元素的存 取 顺序不一致),唯一,元素无索引(解释:其实底层是有索引的,但是我们不能用,所以我们理解为无索引)。
-
双列集合:顶层接口是:Map
- 特点:存储的是键值对元素,其中键具有唯一性,值可以重复,数据结构只针对于键有效。
- 常用子类:
- HashMap:底层数据结构采用是哈希表(数组 + 链表),增删改查都快,线程不安全,效率相对较高。
- TreeMap:底层数据结构采用二叉树,可以对键进行排序,但是这个我们不讲,因为实际开发中的数据从SQL中提取出来的时候就是排好序的。
-
小技巧:
- 集合的顶层都是接口(例如:Collection,List,Set,Map),IO流的顶层都是抽象类(InputStream,OutputStream,Reader,Writer)
- 以后但凡我们学习一个新的继承体系,建议采用:学顶层,用底层的方式,因为顶层定义的是整个继承体系的 共性内容,而底层才是具体的体现,实现
Collection集合
泛型
-
概述:
- 泛型的意思是: 泛指某种具体的数据类型.
-
写法:
<数据类型> -
作用:
泛型一般只和集合相结合使用, 用来限定集合中元素的数据类型的. -
分类: //了解, 前期用不到.
- 泛型类: 把泛型定义到类上, 就叫泛型类
- 泛型方法: 把泛型定义到方法上, 就叫泛型方法
- 泛型接口: 把泛型定义到接口上, 就叫泛型接口
-
注意事项:
1. 泛型必须是 引用类型.
2. 前后泛型必须保持一致, 或者后边的泛型可以省略不写(JDK1.7的新特性: 菱形泛型)
3. 泛型在实际开发中, 一般只和集合相结合使用, 用来限定集合中元素的数据类型的.
4. 泛型一般用字母E, T, K, V表示.
E: Element(元素), T: Type(类型), K: Key(键), V: Value(值)
入门案例
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Collection;
public class Demo04 {
public static void main(String[] args) {
//1.创建集合
//Collection list = new ArrayList(); //不采用泛型,可以添加任意类型的数据
//采用泛型,因为泛型是String,所以该集合只能添加字符串
Collection<String> list = new ArrayList<String>();
//2.添加元素到集合中
list.add("hello");
list.add("world");
list.add("java");
System.out.println(list);
//ArrayList类已经重写了Object#toString()方法
}
}
成员方法
- public boolean add(E e) 添加元素,
- 如果是List体系,返回值都是true,
- 如果是Set体系:重复不添加是false,不重复就添加是true.
- public boolean remove(Object obj) 从集合中移除指定的元素.
- public void clear() 清空集合对象.
- public boolean contains(Object obj) 判断集合中是否包含指定的元素.
- public boolean isEmpty() 判断集合是否为空.
- public int size() 获取集合的长度, 即集合中元素的个数.
案例:测试Collection集合中的成员方法
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Collection;
public class Demo05 {
public static void main(String[] args) {
//1.创建集合对象
Collection<String> list = new ArrayList<String>();
//2.添加元素
//测试:public boolean add(E e) 添加元素, 如果是List体系, 返回值都是true,如果是Set体系:重复不添加是false, 不重复就添加是true.
System.out.println(list.add("hello"));
System.out.println(list.add("java"));
System.out.println(list.add("world"));
//测试:public boolean remove(Object obj) 从集合中移除指定的元素.
System.out.println(list.remove("java"));
//测试:public void clear() 清空集合对象.
list.clear();
//测试:public boolean contains(Object obj) 判断集合中是否包含指定的元素.
System.out.println(list.contains("hello"));
//测试:public boolean isEmpty() 判断集合是否为空.
System.out.println(list.isEmpty());
//测试:public int size() 获取集合的长度, 即集合中元素的个数.
System.out.println(list.size());
//4.打印集合
System.out.println(list);
}
}
集合的核心操作步骤:
- 创建集合对象.
- 创建元素对象.
- 添加元素到集合中
- 遍历集合.
4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator()
即: 根据仓库获取管理该仓库的 仓库管理员.
4.2 (迭代器)判断是否有下一个元素. Iterator#hasNext()
4.3 如果有, 就获取. Iterator#next()
方法解释:
- public Iterator iterator(); 根据集合对象获取其对应的迭代器对象
Iterator接口中的成员方法: - public boolean hasNext(); 判断迭代器中是否有下一个元素.
public E next(); 如果有, 就获取下一个元素.
Collection集合存储字符串,并遍历
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo06 {
public static void main(String[] args) {
//1. 创建集合对象.
Collection<String> list = new ArrayList<>();
//2. 创建元素对象.
//String s1 = "hello";
//3. 添加元素到集合中.
//list.add(s1);
list.add("hello");
list.add("world");
list.add("java");
//4. 遍历集合.
//4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator()
Iterator<String> it = list.iterator();
//4.2 (迭代器)判断是否有下一个元素. Iterator#hasNext()
while (it.hasNext()){
//4.3 如果有, 就获取. Iterator#next()
String s = it.next();
System.out.println(s);
}
}
}
Collection集合存储学生,并遍历
学生类
package com.ithiema.api.demo;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo07 {
public static void main(String[] args) {
//1. 创建集合对象.
Collection<Student> coll = new ArrayList<>();
//2. 创建元素对象.
Student s1 = new Student("张三", 33);
Student s2 = new Student("李四", 34);
Student s3 = new Student("王五", 35);
//3. 添加元素到集合中.
coll.add(s1);
coll.add(s2);
coll.add(s3);
//4. 遍历集合.
//4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator()
Iterator<Student> it = coll.iterator();
//4.2 (迭代器)判断是否有下一个元素. Iterator#hasNext()
while (it.hasNext()) {
//4.3 如果有, 就获取. Iterator#next()
Student s = it.next();
//System.out.println(s);
System.out.println(s.getName() + "," + s.getAge());
}
}
}
List集合
- 概述:
- 它表示单列集合, 也叫有序集合(简称: 序列), 属于Collection集合的子体系.
- 它是java.util包下的接口, 用之前需要先导包.
- 元素特点:
- 有序:指的是元素的存取顺序一致.
- 可重复:指的是能存储重复元素.
- 元素有索引:指的是List集合中每个元素都是有索引的, 索引从 0 开始.
成员方法
- public void add(int index, E element) 在集合的指定位置(索引), 插入指定的元素
- public E remove(int index) 删除指定索引处的元素, 并返回被删除的元素
- public E set(int index, E element) 修改指定索引处的元素为指定的值, 并返回修改前的元素
- public E get(int index) 根据索引, 获取其对应的元素
- 上述4个方法的共同点是: 索引不存在会报 IndexOutOfBoundsException(索引越界异常)
案例
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Demo08 {
public static void main(String[] args) {
//1. 创建集合对象.
List<String> list = new ArrayList<>();
//2. 创建元素对象
//3. 添加元素到集合中.
list.add("hello");
list.add("world");
list.add("java");
list.add("world");
//4. 遍历集合.
//4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator()
Iterator<String> it = list.iterator();
//4.2 (迭代器)判断是否有下一个元素. Iterator#hasNext()
while (it.hasNext()) {
//4.3 如果有, 就获取. Iterator#next()
String s = it.next();
System.out.println(s);
}
}
}
案例:演示List集合存储自定义类型的对象并遍历
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Demo08 {
public static void main(String[] args) {
//1. 创建集合对象.
List<String> list = new ArrayList<>();
//2. 创建元素对象
//3. 添加元素到集合中.
list.add("hello");
list.add("world");
list.add("java");
list.add("world");
//4. 遍历集合.
//4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator()
Iterator<String> it = list.iterator();
//4.2 (迭代器)判断是否有下一个元素. Iterator#hasNext()
while (it.hasNext()) {
//4.3 如果有, 就获取. Iterator#next()
String s = it.next();
System.out.println(s);
}
//方式2:普通for循环遍历
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
}
}
案例:List集合存储学生对象,并遍历
- 遍历方式:
- 普通迭代器
- 普通for循环
学生类
package com.ithiema.api.demo;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类:遍历学生集合
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Demo09 {
public static void main(String[] args) {
//1. 创建集合对象.
List<Student> list = new ArrayList<>();
//2. 创建元素对象.
Student s1 = new Student("张三", 33);
Student s2 = new Student("李四", 34);
Student s3 = new Student("王五", 35);
//3. 添加元素到集合中.
list.add(s1);
list.add(s2);
list.add(s3);
//4. 遍历集合.
//4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator()
Iterator<Student> it = list.iterator();
//4.2 (迭代器)判断是否有下一个元素. Iterator#hasNext()
//快捷键 itit
while (it.hasNext()) {
//4.3 如果有, 就获取. Iterator#next()
Student s = it.next();
System.out.println(s);
}
//方式2:普通for循环 快捷键 itli -> iterator list
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
}
}
列表迭代器
简介
-
概述:
- 它指的是ListIterator接口, 也是Iterator接口的子接口, 表示列表迭代器, 它是独属于List体系的迭代器
-
涉及到的成员方法:
- List集合中的成员方法:
- public ListIterator listIterator(); 根据List集合, 获取其对应的列表迭代器.
- ListIterator中的成员方法:
- public boolean hasNext(); 判断迭代器中是否有下一个元素, 该方法是从 Iterator接口继承过来的
- public E next(); 有就获取下一个元素, 该方法是从 Iterator接口继承过来的
- public boolean hasPrevious(); 判断迭代器中是否有上一个元素, 该方法是ListIterator独有的
- public E previous(); 有就获取上一个元素, 该方法是 ListIterator独有的
- List集合中的成员方法:
-
细节:
- 进行逆向遍历之 前必须先进行一次正向遍历, 且正向和逆向遍历必须使用同一个列表迭代器对象
- 列表迭代器一般用于解决 并发修改异常(ConcurrentModificationException)
案例:列表迭代器详解,正向遍历和逆向遍历
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Demo10 {
public static void main(String[] args) {
//1. 创建集合对象.
List<String> list = new ArrayList<>();
//2. 创建元素对象.
//3. 添加元素到集合中.
list.add("hello");
list.add("world");
list.add("java");
//4. 遍历集合.
//4.1 列表迭代器 正向遍历
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String s = lit.next();
System.out.println(s);
}
System.out.println("------------------------");
//4.2 列表迭代器 逆向遍历
while (lit.hasPrevious()) {
String s = lit.previous();
System.out.println(s);
}
}
}
并发修改异常
- 概述:
- 并发修改异常指的是ConcurrentModificationException, 一般多见于集合部分, 如果业务没有捋顺当, 就有可能出现此问题.
- 问题描述:
- 当用普通迭代器遍历集合的同时, 又往集合中添加元素, 此时就会报: 并发修改异常.
- 注意: 这个仅仅是并发修改异常的产生原因之一, 其他情况也有可能会出现并发修改异常, 你不需要重点关心它是如何出现的, 而是重点关注怎么解决.
- 产生原因:
- 在你通过集合对象获取迭代器对象的时候, 其实普通迭代器底层会有一个变量记录住此时集合中元素的个数, 当集合中实际元素的个数比这个值要大的时候,就会报并发修改异常
- 解决方案:
-
- 通过 列表迭代器 (listIterator)解决, 添加的元素是在当前元素之后.
- 通过 普通for循环 解决, 添加的元素是在集合最后.
- 通过 CopyOnWriteArrayList集合 解决, 添加的元素是在集合最后.
-
案例:并发修改异常
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Demo11 {
public static void main(String[] args) {
//1. 创建集合对象.
List<String> list = new ArrayList<>();
//2. 创建元素对象.
//3. 添加元素到集合中.
list.add("hello");
list.add("world");
list.add("java");
//4.遍历集合
//以下代码会报并发修改异常
/*Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
//4.1 判断s是否是“world”,如果是,就添加“hadoop”
if("world".equals(s)){
list.add("hadoop");
}
}*/
//解决方案1:通过 列表迭代器 解决,添加的元素是当前元素之后
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String s = lit.next();
if ("world".equals(s)){
//list.add("hadoop"); //还是会报并发修改异常
lit.add("hadoop"); //细节:必须用列表迭代器 的添加元素的方法
}
}
//解决方案2:通过 普通for循环 解决,添加的元素是在集合后面
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if("world".equals(s)){
list.add("hadoop");
}
}
//5.打印集合
System.out.println(list);
}
}
解决方案3:通过 CopyOnWriteArrayList集合 解决
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Demo12 {
public static void main(String[] args) {
//1. 创建集合对象.
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
//2. 创建元素对象.
//3. 添加元素到集合中.
list.add("hello");
list.add("world");
list.add("java");
//4.遍历集合
//以下代码会报并发修改异常
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
//4.1 判断s是否是“world”,如果是,就添加“hadoop”
if("world".equals(s)){
list.add("hadoop");
}
}
//5.打印集合
System.out.println(list);
}
}
增强for
-
概述
-
它是JDK1.5的新特性, 用来简化遍历数组和集合操作的
-
格式
-
for(数据类型 变量名: 要遍历的集合或者数组) { //变量名其实就代表数组或者集合中的每一个元素. }
-
-
注意事项:
- 增强for 的本质就是一个 普通迭代器.
- 你用增强for 遍历集合的同时往集合中加元素了, 也会报并发修改异常
-
快捷键:iter
案例:增强for 遍历数组和列表
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.List;
public class Demo14 {
public static void main(String[] args) {
int[] arr = {11, 22, 33, 44, 55};
for (int i : arr) {
System.out.println(i);
}
System.out.println("---------------------");
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
for (String s : list) {
System.out.println(s);
}
}
}
常见的数据结构
特点:
- 栈:先进后出,后进先出。 作用:1.存储所用的局部变量2.所用代码执行。
- 队列:先进先出,后进后出。 存数据动作叫 入队,取数据动作叫 出队。
- 数组:查询(修改)快,增删慢。
- 链表:查询(修改)慢,增删快。
- 节点:由数值域 和地址域组成
- 链表:由节点组成的一条链子。
- 根据节点不同,链表主要分为:
1. 单向链表
2. 单向循环列表
3. 双向链表
4. 双向循环链
- 根据节点不同,链表主要分为:
栈和队列图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mrl4yeko-1669232941564)(F:\BigData\java\图片\day10图片\01栈和队列.png)]
数组和链表图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IilQEVaG-1669232941564)(F:\BigData\java\图片\day10图片\02数组和链表.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GdyCSCoe-1669232941565)(F:\BigData\java\图片\day10图片\链表.png)]
List集合的子类
常用的子类
List集合是一个接口, 它的常用子类有两个, 分别是ArrayList, LinkedList.
- ArrayList集合的特点:底层数据结构是数组, 查询和修改快, 增删慢.
- LinkedList集合的特点:底层数据结构是链表, 查询和修改慢, 增删快.
注意: 它们的相同点是, 都是有序的, 而且可以存储重复元素.
ArrayList集合
案例1_ArrayList集合存储字符串并遍历
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
public class Demo15 {
public static void main(String[] args) {
//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2. 创建元素对象.
//3. 添加元素到集合中.
list.add("hello");
list.add("world");
list.add("java");
//4. 遍历集合.
//方式1:增强for iter
for (String s : list) {
System.out.println(s);
}
System.out.println("----------------------------");
//方式2:普通for循环 itli
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
System.out.println("----------------------------");
//方式3:列表迭代器 itit
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String s = lit.next();
System.out.println(s);
}
System.out.println("----------------------------");
//方式4:普通迭代器 itit
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
System.out.println("----------------------------");
//方式5:转数组遍历,凑数的,实际开发不用
Object[] obj = list.toArray();
for (Object o : obj) {
System.out.println(o);
}
}
}
案例2_ArrayList集合存储学生并遍历
学生类
package com.ithiema.api.demo;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类,遍历集合
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class Demo16 {
public static void main(String[] args) {
//1.创建集合对象
ArrayList<Student> list = new ArrayList<>();
//2. 创建元素对象.
Student s1 = new Student("张三", 23);
Student s2 = new Student("李四", 24);
Student s3 = new Student("王五", 25);
//3. 添加元素到集合中.
list.add(s1);
list.add(s2);
list.add(s3);
//4. 遍历集合.
//方式1:增强for iter
for (Student s : list) {
System.out.println(s);
}
System.out.println("----------------------------");
//方式2:普通for循环 itli
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
System.out.println("----------------------------");
//方式3:列表迭代器 itit
ListIterator<Student> lit = list.listIterator();
while (lit.hasNext()) {
Student s = lit.next();
System.out.println(s);
}
System.out.println("----------------------------");
//方式4:普通迭代器 itit
Iterator<Student> it = list.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s);
}
System.out.println("----------------------------");
//方式5:转数组遍历,凑数的,实际开发不用
Object[] objects = list.toArray();
for (Object object : objects) {
System.out.println(object);
}
}
}
LinkedList集合
- 概述
- 它属于List体系, 元素特点是: 有序, 可重复, 元素有索引.
- 它的底层数据结构采用的
- 是链表, 所以查询修改相对较慢, 增删相对较快.
成员方法
- public void addFirst(E e) 往列表的开头插入指定的元素
- public void addLast(E e) 往列表的末尾插入指定的元素
- public E removeFirst() 删除列表中的第一个元素, 并返回被删除的元素
- public E removeLast() 删除列表中的最后一个元素, 并返回被删除的元素.
- public E getFirst() 返回列表的第一个元素
- public E getLast() 返回列表的最后一个元素
案例一: LinkedList入门
package com.itheima.demo06_linkedlist;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
//案例: 演示LinkedList集合入门.
//概述: LinkedList集合解释: 底层数据结构是链表, 所以增删快, 查询修改慢.
public class Demo01 {
public static void main(String[] args) {
//1.创建LinkedList集合对象, 存储字符串数据: "hello", "world", "java"
LinkedList<String> list = new LinkedList<>();
list.add("hello");
list.add("world");
list.add("java");
//2.遍历LinkedList集合.
//方式一: 普通的迭代器.
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
System.out.println("--------------------------------");
//方式二: 普通for
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
System.out.println("--------------------------------");
//方式三: 增强for
for (String s : list) {
System.out.println(s);
}
System.out.println("--------------------------------");
//方式四: 列表迭代器
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String s = lit.next();
System.out.println(s);
}
System.out.println("--------------------------------");
//方式五: 转数组遍历
Object[] objs = list.toArray();
for (Object obj : objs) {
System.out.println(obj);
}
}
}
案例二:LinkedList的特有方法
package com.itheima.demo06_linkedlist;
import java.util.LinkedList;
//案例: 演示LinkedList集合独有的方法.
public class Demo02 {
public static void main(String[] args) {
//1.创建LinkedList集合对象, 存储字符串数据: "hello", "world", "java"
LinkedList<String> list = new LinkedList<>();
list.add("hello");
list.add("world");
list.add("java");
//2.分别演示上述的6个方法.
//演示添加.
/*list.addFirst("黑马程序员");
//list.addLast("传智播客");
list.add("传智播客");*/
//演示删除元素
//System.out.println(list.removeFirst());
//System.out.println(list.removeLast());
//演示获取
System.out.println(list.getFirst());
System.out.println(list.getLast());
//3, 打印集合
System.out.println("list: " + list);
}
}
Set集合
-
概述:
- 它属于单列集合, 是Collection集合的子体系.
-
元素特点:
- 无序(元素的存取顺序不一致), 唯一, 元素无索引.
-
常用子类:
- HashSet:底层数据结构采用哈希表, 增删改查都快, 线程不安全, 效率高.
案例: 演示Set集合入门
package com.itheima.demo01_set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
//案例: 演示Set集合入门
//记忆: Set集合是Collection的子接口, 它的元素特点是: 无序, 唯一.
//Set集合其实底层也是有索引的, 只是不对外暴漏, 作为程序员我们无法直接使用Set集合的索引.
public class Demo01 {
public static void main(String[] args) {
//1.创建Set集合对象, 存储字符串数据: "hello", "world", "java", "world"
Set<String> hs = new HashSet<>();
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
//2.通过两种方式, 遍历Set集合.
//方式一: 普通迭代器
Iterator<String> it = hs.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
System.out.println("--------------------");
//方式二: 增强for
for (String s : hs) {
System.out.println(s);
}
System.out.println("--------------------");
//方式三: 转数组遍历, 了解.
Object[] objs = hs.toArray();
for (Object obj : objs) {
System.out.println(obj);
}
}
}
哈希值
-
概述
- 所谓的哈希值, 就是JDK根据对象的地址, 属性等各种信息算出来的一个 int 类型的值.
-
问题: 如何获取哈希值呢?
- 答案: 可以通过Object#hashCode()方法实现.
public int hashCode(); 获取对象的哈希值.
- 答案: 可以通过Object#hashCode()方法实现.
-
结论:
- 实际开发中, 我们认为, 如果同一个类的两个对象, 各个属性都相同, 那么它们就是同一个对象.
- 同一对象哈希值肯定相同, 不同对象哈希值一般不同.
3. 同一个对象多次调用 hashCode()方法, 获取的结果也是一样的.
4. 默认情况下, 不同对象哈希值是不同的, 但是我们可以手动重写Object#hashCode(), 实现: 不同对象的哈希值也是相同的.
但是没有什么意义, 一般也不会这么做.
5. 原则: 保证同一对象哈希值必须相同, 不同对象哈希值尽量不同.
6. 实际开发做法: 快捷键生成, 重写Object#hashCode().
案例: 哈希值入门
定义JavaBean类, 表示学生类.
package com.itheima.pojo;
//定义JavaBean类, 表示学生类.
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
package com.itheima.demo01_set;
import com.itheima.pojo.Student;
//案例: 哈希值入门.
public class Demo02 {
public static void main(String[] args) {
//1.定义学生类, 属性为姓名和年龄.
//2.在测试类的main方法中, 创建两个学生对象, 分别获取它们的哈希值, 并打印.
Student s1 = new Student("刘亦菲", 33);
Student s2 = new Student("刘亦菲", 33);
Student s3 = new Student("赵丽颖", 31);
//3.测试: 重写Object#hashCode()方法, 实现不同对象的哈希值也是相同的.
System.out.println("s1: " + s1.hashCode()); //s1: 646483581
System.out.println("s2: " + s2.hashCode()); //s2: 646483581
System.out.println("s3: " + s3.hashCode()); //s3: 1099280305
//4.测试: 同一对象哈希值肯定相同, 不同对象哈希值一般不同.
//重地和通话, 儿女和农丰 哈希值相同情况
System.out.println("重地abc".hashCode());
System.out.println("通话abc".hashCode());
System.out.println("-------------------------");
System.out.println("儿女".hashCode());
System.out.println("农丰".hashCode());
}
}
HashSet集合
-
简介
- 它属于Set体系, 元素特点是: 无序, 唯一, 元素无索引.
底层数据结构采用哈希表(数组+链表), 增删改查都快, 线程不安全, 效率高.
- 它属于Set体系, 元素特点是: 无序, 唯一, 元素无索引.
-
问题: HashSet集合保证元素唯一性的原理是什么?
- 答案:HashSet集合保证元素的唯一性依赖 **hashCode()和equals()**方法,
- 即: 存储什么元素, 只要让该元素的类型重写这两个方法即可,快捷键生成
- 答案:HashSet集合保证元素的唯一性依赖 **hashCode()和equals()**方法,
案例: 演示HashSet集合的使用
package com.itheima.demo01_set;
import java.util.HashSet;
import java.util.Iterator;
//案例: 演示HashSet集合的使用.
//记忆: HashSet集合的特点是: 无序, 唯一, 元素无索引, 它的底层数据结构是: 哈希表(数组 + 链表).
public class Demo03 {
public static void main(String[] args) {
//1.定义HashSet集合, 存储字符串"hello", "world", "java", "world"
HashSet<String> hs = new HashSet<>();
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
hs.add(null);
hs.add(null);
//2.遍历HashSet集合, 打印每一个元素值, 并观察程序的运行结果.
//方式一: 增强for
for (String h : hs) {
System.out.println(h);
}
System.out.println("-------------------");
//方式二: 普通的迭代器
Iterator<String> it = hs.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
案例: 演示HashSet存储在自定义对象, 并保证唯一性
JavaBean类:学生类
package com.itheima.pojo;
//定义JavaBean类, 表示学生类.
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类:
package com.itheima.demo01_set;
import com.itheima.pojo.Student;
import java.util.HashSet;
//案例: 演示HashSet存储在自定义对象, 并保证唯一性.
//细节: 记得在自定义类中重写hashCode()和equals()方法.
public class Demo04 {
public static void main(String[] args) {
//1.定义学生类, 属性为姓名和年龄.
//2.创建HashSet集合, 用来存储学生对象, 并往其中添加3个学生的信息.
HashSet<Student> hs = new HashSet<>();
hs.add(new Student("刘亦菲", 33));
hs.add(new Student("赵丽颖", 31));
hs.add(new Student("赵丽颖", 31));
hs.add(new Student("高圆圆", 35));
hs.add(new Student("高圆圆", 35));
//3.遍历集合, 并把结果打印到控制台上.
for (Student student : hs) {
System.out.println(student);
}
//结果:唯一性
//Student{name='赵丽颖', age=31}
//Student{name='刘亦菲', age=33}
//Student{name='高圆圆', age=35}
}
}
LinkedHashSet集合
- 简介
- 它是HashSet集合的子类, 元素特点是: 有序, 唯一.
- 它的底层数据结构采用的是 链表 + 哈希表的形式, 由链表保证有序, 哈希表保证唯一.
package com.itheima.demo01_set;
import java.util.LinkedHashSet;
//案例: 演示LinkedHashSet集合.
/*
LinkedHashSet介绍:
它是HashSet集合的子类, 特点是: 有序, 唯一.
*/
public class Demo05 {
public static void main(String[] args) {
//1.创建LinkedHashSet集合对象, 存储字符串"hello", "world", "java", "world"
LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.add("hello");
lhs.add("world");
lhs.add("java");
lhs.add("world");
//2.遍历集合, 并将结果打印到控制台上.
for (String s : lhs) {
System.out.println(s);
}
//hello
//world
//java
}
}
可变参数
-
概述:
-
它是JDK1.5的特性, 表示参数的个数是可以变化的, 多用于 方法的形参列表
-
格式:
-
public static int getSum(int... a) 即: 在数据类型的后边加上 3个点
-
-
注意事项:
- 可变参数的本质就是一个 数组.
- 方法的形参列表有且只能有一个可变参, 且可变参数必须放到形参列表的最后.
- 可变参数的参数个数至少 0 个, 至多 无数个.
案例: 演示可变参数
package com.itheima.demo02_change;
//案例: 演示可变参数.
/*
可变参数简介:
概述:
它是JDK1.5的特性, 它的本质就是一个数组.
格式:
public static int getSum(int... a)
即: 在数据类型的后边加上 3个点
注意事项:
1. 可变参数的本质就是一个数组.
2. 如果一个方法有多个参数, 其中包含可变参数, 那么可变参数要放最后.
即: 方法的形参列表有且只能有一个可变参数, 且可变参数要放最后.
*/
public class Demo01 {
public static void main(String[] args) {
//2.在main方法中, 调用getSum()方法.
int sum = getSum(1, 2, 3, 4, 5, 11);
System.out.println(sum);
}
//1.定义getSum()方法, 用来获取n个整数的和(n可能是任意的一个数字).
//public static int getSum(String b, int... a) { //至少传入0个整数, 至多传入n个整数.
public static int getSum(int... a) { //至少传入0个整数, 至多传入n个整数.
int sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
}
return sum;
}
}
Map集合
- 概述:
- 它表示双列集合的顶层接口, 存储的是 键值对元素, 其中键具有唯一性, 值可以重复.
- 数据结构只针对于 键 有效, 且元素是 无序 的.
成员方法
-
public V put(K key,V value) 添加元素, 键存在就用新值覆盖旧值并返回旧值, 键不存在就直接添加并返回null.
-
public V remove(Object key) 根据键删除键值对元素, 返回删除之前的 值.
-
public void clear() 移除所有的键值对元素
-
public boolean containsKey(Object key) 判断集合是否包含指定的键
-
public boolean containsValue(Object value) 判断集合是否包含指定的值
-
public boolean isEmpty() 判断集合是否为空, 当且仅当长度为0返回true, 否则false
-
public int size() 集合的长度,也就是集合中键值对的个数
Map集合中的常用成员方法
package com.itheima.demo03_map;
import java.util.HashMap;
import java.util.Map;
//案例: 演示Map集合中的常用成员方法
public class Demo02 {
public static void main(String[] args) {
//1.定义Map集合, 键是丈夫, 值是妻子. (键值都是字符串类型).
Map<String, String> hm = new HashMap<>();
//2.分别测试上述的7个方法.
//2.1 测试 V put(K key,V value) 添加元素
hm.put("杨过", "大雕");
hm.put("尹志平", "小龙女");
hm.put("张无忌", "赵敏");
//2.2 测试V remove(Object key) 根据键删除键值对元素
String yg = hm.remove("杨过123");
System.out.println("yg: " + yg);
//2.3 测试 void clear()
hm.clear();
//2.4 测试 boolean containsKey(Object key) 判断集合是否包含指定的键
System.out.println(hm.containsKey("杨过"));
System.out.println(hm.containsKey("乔峰"));
//2.5 测试 boolean containsValue(Object value) 判断集合是否包含指定的值
System.out.println(hm.containsValue("小龙女"));
System.out.println(hm.containsValue("蛛儿"));
//2.6 测试 boolean isEmpty() 判断集合是否为空
System.out.println(hm.isEmpty());
//2.7 测试 int size() 集合的长度,也就是集合中键值对的个数
System.out.println(hm.size());
//2.8 打印双列集合.
System.out.println("hm: " + hm);
}
}
获取功能相关方法
- public Set keySet(); 获取所有键的集合,因为Map的键具有唯一性,所以返回值Set集合
- public Collection values(); 获取所有值的集合,因为Map的值可以重复,所以返回值是Collection
- public V get(K key); 根据键获取其对应的值.
- public Set<Map.Entry<K, V>> entrySet(); 获取所有键值对对象的集合.
Map集合的获取功能
package com.itheima.demo03_map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
//案例: 演示Map集合的获取功能
/*
涉及到的Map集合中的方法:
public V get(K key) 根据键获取其对应的值.
public Set<K> keySet(); 获取所有的键, 因为Map的键具有唯一性, 所以返回值Set集合.
public Collection<V> values(); 获取所有的值, 因为Map的值可以重复, 所以返回值是Collection
*/
public class Demo03 {
public static void main(String[] args) {
//1.定义Map集合, 键是丈夫, 值是妻子. (键值都是字符串类型).
HashMap<String, String> hm = new HashMap<>();
hm.put("乔峰", "阿朱");
hm.put("虚竹", "梦姑");
hm.put("段誉", "王语嫣");
//2.先通过代码测试上述的3个方法. 即: get(), keySet(), values()
//2.1 测试 public V get(K key) 根据键获取其对应的值.
//分解版
String value1 = hm.get("乔峰");
System.out.println(value1);
//合并版
System.out.println(hm.get("乔峰"));
System.out.println(hm.get("慕容复"));
//2.2 测试 public Set<K> keySet(); 获取所有的键, 因为Map的键具有唯一性, 所以返回值Set集合.
Set<String> keys = hm.keySet();
for (String key : keys) {
System.out.println(key);
}
System.out.println("-------------------");
//2.3 测试 public Collection<V> values(); 获取所有的值, 因为Map的值可以重复, 所以返回值是Collection
Collection<String> values = hm.values();
for (String value : values) {
System.out.println(value);
}
//测试public Set<Map.Entry<K, V>> entrySet(); 获取所有键值对 对象的集合.
System.out.println(hm.entrySet());
}
}
Map集合的遍历方式一: 根据键获取其对应的值
- 具体步骤:
1. 获取到所有的键的集合. Map#keySet()
2. 遍历, 获取到每一个键. 增强for, 迭代器
3. 根据键获取其对应的值. Map#get(K key)
package com.itheima.demo03_map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
//案例: 演示Map集合的遍历方式一: 根据键获取其对应的值.
public class Demo04 {
public static void main(String[] args) {
//1.定义Map集合, 键是丈夫, 值是妻子. (键值都是字符串类型).
HashMap<String, String> hm = new HashMap<>();
//2.往集合中添加3对键值对元素.
hm.put("乔峰", "阿朱");
hm.put("虚竹", "梦姑");
hm.put("段誉", "王语嫣");
//3.遍历Map集合.
//3.1. 获取到所有的键的集合.
Set<String> keys = hm.keySet();
//方式一: 增强for
//3.2 遍历, 获取到每一个键
for (String key : keys) {
//3.3 根据键获取其对应的值.
String value = hm.get(key);
System.out.println(key + "..." + value);
}
System.out.println("--------------------------");
//方式二: 普通迭代器
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String key = it.next();
System.out.println(key + "..." + hm.get(key));
}
}
}
Map集合的遍历方式二: 根据键值对对象获取其对应的键和值
- 具体步骤:
1. 获取到所有的 键值对对象 的集合.
2. 遍 - Map#entrySet(), 即: public Set<Map.Entry<K, V>> entrySet(); Map集合中获取所有键值对对象的方法.
2. 遍历, 获取到每一个 键值对对象. 增强for, 迭代器
3. 根据 键值对对象 获取其对应的键和值. Map.Entry中的方法:getKey(), getValue() - 小细节:
Map.Entry 是内部接口的写法, 即: Map是外部接口, Entry是Map接口中的内部接口.
package com.itheima.demo03_map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
//案例: 演示Map集合的遍历方式二: 根据键值对对象获取其对应的键和值.
/*
具体步骤:
1. 获取到所有的 键值对对象 的集合. Map#entrySet(), 即: public Set<Map.Entry<K, V>> entrySet(); Map集合中获取所有键值对对象的方法.
2. 遍历, 获取到每一个 键值对对象. 增强for, 迭代器
3. 根据 键值对对象 获取其对应的键和值. Map.Entry中的方法: getKey(), getValue()
小细节:
Map.Entry 是内部接口的写法, 即: Map是外部接口, Entry是Map接口中的内部接口.
*/
public class Demo05 {
public static void main(String[] args) {
//1.定义Map集合, 键是丈夫, 值是妻子. (键值都是字符串类型).
HashMap<String, String> hm = new HashMap<>();
//2.往集合中添加3对键值对元素.
hm.put("乔峰", "阿朱");
hm.put("虚竹", "梦姑");
hm.put("段誉", "王语嫣");
//3.遍历Map集合.
//方式一: 增强for
//3.1 获取到所有的 键值对对象 的集合.
Set<Map.Entry<String, String>> entrys = hm.entrySet(); //需要导入: import java.util.Map;
//Set<Entry<String, String>> entrys = hm.entrySet(); //需要导入: import java.util.Map.Entry;
//3.2 遍历, 获取到每一个 键值对对象.
//Map.Entry<String, String>: 就是键值对对象的数据类型
//String 就是字符串的数据类型
for (Map.Entry<String, String> entry : entrys) {
//3.3 根据 键值对对象 获取其对应的键和值.
System.out.println(entry.getKey() + "..." + entry.getValue());
}
System.out.println("----------------------------");
//方式二: 迭代器
//3.1 获取到所有的 键值对对象 的集合.
Iterator<Map.Entry<String, String>> it = entrys.iterator();
//3.2 遍历, 获取到每一个 键值对对象.
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
//3.3 根据 键值对对象 获取其对应的键和值.
System.out.println(entry.getKey() + "..." + entry.getValue());
}
}
}
Map集合案例: 键是String, 值是Student
JavaBean类:学生类
package com.ithiema.api.demo;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
package com.itheima.demo04_exercise;
import com.itheima.pojo.Student;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//Map集合案例: 键是String, 值是Student
public class Demo01 {
public static void main(String[] args) {
//1.创建HashMap集合, 键是学号(String), 值是学生对象(Student).
HashMap<String, Student> hm = new HashMap<>();
//2.往HashMap集合中添加3组数据.
hm.put("黑马001", new Student("乔峰",36));
hm.put("黑马002", new Student("虚竹",29));
hm.put("黑马003", new Student("段誉",21));
//hm.put("黑马003", new Student("虚竹",29));
//3.通过两种方式, 遍历HashMap集合.
//方式一: 根据键获取其对应的键和值.
Set<String> keys = hm.keySet();
for (String key : keys) {
System.out.println(key + "..." + hm.get(key));
}
System.out.println("-------------------------");
//方式二: 根据键值对获取其对应的键和值.
Set<Map.Entry<String, Student>> entrys = hm.entrySet();
for (Map.Entry<String, Student> entry : entrys) {
System.out.println(entry.getKey() + "..." + entry.getValue());
}
}
}
Map集合案例: 键是Student, 值是String
package com.itheima.demo04_exercise;
import com.itheima.pojo.Student;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//Map集合案例: 键是Student, 值是String
//结论: HashMap保证键的唯一性依赖: hashCode(), equals()方法.
public class Demo02 {
public static void main(String[] args) {
//1.创建HashMap集合, 键是学生对象(Student), 值是居住地(String).
HashMap<Student, String> hm = new HashMap<>();
//2.往HashMap集合中添加3组数据.
hm.put(new Student("乔峰", 36), "北京");
hm.put(new Student("虚竹", 29), "上海");
hm.put(new Student("段誉", 21), "广州");
//hm.put(new Student("段誉", 21), "深圳");
//3.通过两种方式, 遍历HashMap集合.
//方式一: 根据键获取其对应的键和值.
Set<Student> keys = hm.keySet();
for (Student key : keys) {
System.out.println(key + "..." + hm.get(key));
}
System.out.println("-------------------------");
//方式二: 根据键值对获取其对应的键和值.
Set<Map.Entry<Student, String>> entrys = hm.entrySet();
for (Map.Entry<Student, String> entry : entrys) {
System.out.println(entry.getKey() + "..." + entry.getValue());
}
}
}
Map案例: ArrayList嵌套HashMap集合
package com.itheima.demo04_exercise;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//Map案例: ArrayList嵌套HashMap集合.
public class Demo03 {
public static void main(String[] args) {
//1.定义ArrayList<HashMap<String, String>>集合, 存储三个元素, 每个元素都是一个双列集合, 具体如下:
ArrayList<HashMap<String, String>> list = new ArrayList<>();
//2.第一个双列集合, 记录的信息如下:
// 孙策 大乔
// 周瑜 小乔
HashMap<String, String> sgyy = new HashMap<>();
sgyy.put("孙策", "大乔");
sgyy.put("周瑜", "小乔");
//3.第二个双列集合, 记录的信息如下:
// 郭靖 黄蓉
// 杨过 大雕
HashMap<String, String> sdxl = new HashMap<>();
sdxl.put("郭靖", "黄蓉");
sdxl.put("杨过", "大雕");
//4.第三个双列集合, 记录的信息如下:
// 令狐冲 任盈盈
// 林平之 岳灵珊
HashMap<String, String> xajh = new HashMap<>();
xajh.put("令狐冲", "任盈盈");
xajh.put("林平之", "岳灵珊");
//5.把上述的三个双列集合当做元素对象, 添加到ArrayList集合中.
list.add(sgyy);
list.add(sdxl);
list.add(xajh);
//6.遍历ArrayList集合, 输出每个元素.
//方式一: 根据键获取值.
//6.1 遍历ArrayList集合, 获取每一个元素: HashMap<String, String>
for (HashMap<String, String> hm : list) {
//6.2 因为ArrayList集合的每个元素还是一个双列集合, 所以我们接着遍历.
Set<String> keys = hm.keySet();
//6.3 遍历, 获取双列集合的每一个键.
for (String key : keys) {
System.out.println(key + "..." + hm.get(key));
}
System.out.println();
}
System.out.println("---------------------------");
//方式二: 根据键值对对象获取键和值.
//6.1 遍历ArrayList集合, 获取每一个元素: HashMap<String, String>
for (HashMap<String, String> hm : list) {
//6.2 因为ArrayList集合的每个元素还是一个双列集合, 所以我们接着遍历.
Set<Map.Entry<String, String>> entrys = hm.entrySet();
//6.3 遍历, 获取双列集合的每一个键.
for (Map.Entry<String, String> entry : entrys) {
System.out.println(entry.getKey() + "..." + entry.getValue());
}
System.out.println();
}
}
}
Map案例: HashMap嵌套ArrayList
package com.ithiema.api.demo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//Map案例: HashMap嵌套ArrayList
public class Demo18 {
public static void main(String[] args) {
//1.创建一个HashMap集合,存储三个键值对元素,键表示书名(String),值是书中人物名(ArrayList<String>)
HashMap<String, ArrayList<String>> list = new HashMap<>();
//2.第一个ArrayList集合的元素: (三国演义)
// 诸葛亮
// 赵云
ArrayList<String> sgyy = new ArrayList<>();
sgyy.add("诸葛亮");
sgyy.add("赵云");
//3.第二个ArrayList集合的元素: (西游记)
// 唐僧
// 孙悟空
ArrayList<String> xyj = new ArrayList<>();
xyj.add("唐僧");
xyj.add("孙悟空");
//4.第三个ArrayList集合的元素: (水浒传)
// 武松
// 鲁智深
ArrayList<String> shz = new ArrayList<>();
shz.add("武松");
shz.add("鲁智深");
//5. 把元素填充到双列集合中.
list.put("三国演义", sgyy);
list.put("西游记", xyj);
list.put("水浒传", shz);
//6.遍历HashMap集合, 输出每一个元素.
//方式一: 根据键获取值.
//6.1 获取所有的键.
Set<String> keys = list.keySet();
//6.2 遍历, 获取到每一个键: String
for (String key : keys) {
//6.3 根据键获取其对应的值: ArrayList<String>, 接着遍历.
ArrayList<String> values = list.get(key);
//6.5 因为值还是一个单列集合, 所以接着遍历.
for (String value : values) {
//6.6 获取到每一个具体的值, 然后输出.
System.out.println(key + " " + value);
}
}
System.out.println("-----------------");
//方式二: 根据键值对对象获取键和值.
//6.1 获取所有的键.
Set<Map.Entry<String, ArrayList<String>>> entrys = list.entrySet();
//6.2 遍历, 获取到每一个键: String
for (Map.Entry<String, ArrayList<String>> entry : entrys) {
String key = entry.getKey();
//6.3 获取值: ArrayList<String>, 接着遍历.
ArrayList<String> values = entry.getValue();
//6.4 因为值还是一个单列集合, 所以接着遍历.
for (String value : values) {
//6.5 获取到每一个具体的值, 然后输出.
System.out.println(key + " " + value);
}
}
}
}
Map集合案例: 通过字符串中每个字符的次数
代码实现_详细版
package com.itheima.demo04_exercise;
import java.util.*;
//Map集合案例: 通过字符串中每个字符的次数.
public class Demo05_代码实现_详细版 {
public static void main(String[] args) {
/*
1.键盘录入一个字符串,要求统计字符串中每个字符出现的次数。
2.举例:键盘录入“aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)”
*/
//1. 创建Scanner对象.
Scanner sc = new Scanner(System.in);
//2. 提示用户录入一个字符串, 并接收.
System.out.println("请录入一个字符串: ");
String str = sc.nextLine();
//3. 定义Map集合, 字符做键, 该字符对应的次数作为值. 即: HashMap<Character, Integer> a:3, b:1
Map<Character, Integer> map = new HashMap<>();
//Map<Character, Integer> map = new TreeMap<>();
//4. 把字符串转成字符数组.
char[] chs = str.toCharArray();
//5. 遍历, 获取到每一个字符.
for (char ch : chs) {
//6. 判断该字符在双列集合中是否存在.
if (!map.containsKey(ch)) {
//6.1 不存在, 说明该字符是第一次出现, 就将其次数记录为1.
map.put(ch, 1);
} else {
//6.2 存在, 说明该字符不是第一次出现, 就将其次数+1, 然后重新存储.
int count = map.get(ch);
map.put(ch, count + 1);
}
}
//已知格式: a:3, b:1 目标格式: a(5)b(4)c(3)d(2)e(1)
//7. 走到这里, Map集合记录的就是我们要的结果, 将其拼接成字符串.
StringBuilder sb = new StringBuilder();
Set<Character> keys = map.keySet();
for (Character key : keys) {
int count = map.get(key);
sb.append(key).append("(").append(count).append(")");
}
//8. 打印结果.
String result = sb.toString();
System.out.println(result);
}
}
合并版
package com.itheima.demo04_exercise;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
//Map集合案例: 通过字符串中每个字符的次数.
public class Demo05_代码实现_合并版 {
public static void main(String[] args) {
/*
1.键盘录入一个字符串,要求统计字符串中每个字符出现的次数。
2.举例:键盘录入“aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)”
*/
//1. 定义Map集合, 字符做键, 该字符对应的次数作为值. 即: HashMap<Character, Integer> a:3, b:1
Map<Character, Integer> map = new HashMap<>();
//2. 提示录入, 然后遍历, 获取到每一个字符.
System.out.println("请录入一个字符串: ");
for (char ch : new Scanner(System.in).nextLine().toCharArray())
//6. 判断该字符在双列集合中是否存在.
map.put(ch, !map.containsKey(ch) ? 1 : map.get(ch) + 1);
//已知格式: a:3, b:1 目标格式: a(5)b(4)c(3)d(2)e(1)
//7. 走到这里, Map集合记录的就是我们要的结果, 将其拼接成字符串.
StringBuilder sb = new StringBuilder();
for (Character key : map.keySet())
sb.append(key).append("(").append( map.get(key)).append(")");
//8. 打印结果.
System.out.println(sb);
}
}
Collections工具类
- 概述
- 它是用来操作集合的工具类,提供了大量针对于集合操作的方法
成员方法
-
public static void sort(List list) 将指定的列表按升序排序
-
public static void reverse(List<?> list) 反转指定列表中的元素的顺序
-
public static void shuffle(List<?> list) 随机置换
-
public static E max(List<?> list) 获取集合元素的最大值
-
public static E min(List<?> list) 获取集合元素的最小值
-
对集合元素降序排列. 先升序, 后反转.
Collections.sort(list);
Collections.reverse(list);
方法详解案例
package com.itheima.demo05_collections;
import java.util.ArrayList;
import java.util.Collections;
public class Demo01 {
public static void main(String[] args) {
//1.定义ArrayList集合, 存储5个整数.
ArrayList<Integer> list = new ArrayList<>();
list.add(11);
list.add(44);
list.add(33);
list.add(22);
list.add(55);
//2.分别测试上述的3个方法.
//测试: public static void sort(List list) 对列表排序, 默认是升序
Collections.sort(list);
//测试: public static void reverse(List list) 反转列表元素
Collections.reverse(list);
//对集合元素降序排列. 先升序, 后反转.
Collections.sort(list);
Collections.reverse(list);
//测试 public static void shuffle(List list) 随机置换, 相当于洗牌.
Collections.shuffle(list);
//max(), min()
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
//3. 打印List集合
System.out.println("list: " + list);
}
}
案例: 模拟斗地主发牌_无序的牌
package com.itheima.demo04_exercise;
import java.util.ArrayList;
import java.util.Collections;
//案例: 模拟斗地主发牌
public class Demo06_Poker_无序的牌 {
public static void main(String[] args) {
//1. 买牌.
//搞一个牌盒, 用来装扑克牌.
ArrayList<String> pokers = new ArrayList<>();
//填充牌
String[] colors = {"♥", "♠", "♦", "♣"};
String[] numbers = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
//遍历花色集合
for (String color : colors) { //"♥", "♠", "♦", "♣"
//遍历点数集合
for (String number : numbers) { //"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"
pokers.add(color + number);
}
}
//添加大小王
pokers.add("小王");
pokers.add("大王");
//2. 洗牌.
Collections.shuffle(pokers);
//3. 发牌. 三个玩家, 一个底牌
ArrayList<String> lyf = new ArrayList<>();
ArrayList<String> zly = new ArrayList<>();
ArrayList<String> hg = new ArrayList<>();
ArrayList<String> dipai = new ArrayList<>();
//发牌思路: 1. 最后三张作为底牌. 2. 牌的索引是0到53的, 依次和3取余, 余数为0给第一个人, 余数为1给第二个人, 余数为3给第三个人.
for (int i = 0; i < pokers.size(); i++) {
String poker = pokers.get(i); //就是每一张牌.
if (i >= pokers.size() - 3) { //51, 52, 53
dipai.add(poker);
} else if (i % 3 == 0) {
lyf.add(poker);
}else if (i % 3 == 1) {
zly.add(poker);
}else if (i % 3 == 2) {
hg.add(poker);
}
}
//4, 看牌.
lookPoker("刘亦菲", lyf);
lookPoker("赵丽颖", zly);
lookPoker("夯哥", hg);
lookPoker("底牌", dipai);
}
//4. 定义方法, 用来看牌
public static void lookPoker(String name, ArrayList<String> list) {
System.out.print(name + "的牌是: ");
for (String poker : list) {
System.out.print(poker + " ");
}
System.out.println();
}
}
案例: 模拟斗地主发牌_有序的牌
package com.itheima.demo04_exercise;
import java.util.*;
//案例: 模拟斗地主发牌_有序的牌
public class Demo07_有序的牌 {
public static void main(String[] args) {
//1. 买牌.
//1.1 造牌盒, 键: 牌的编号, 值: 对应的牌, 规则: 编号越小, 牌越小.
HashMap<Integer, String> pokers = new HashMap<>();
//1.2 造牌盒, 记录所有牌的编号.
ArrayList<Integer> list = new ArrayList<>();
//1.3 造牌.
String[] colors = {"♥", "♠", "♦", "♣"};
String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
//1.4 定义变量, 记录牌的索引
int index = 0;
//遍历点数集合
for (String number : numbers) { //"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"
//遍历花色集合
for (String color : colors) { //"♥", "♠", "♦", "♣"
pokers.put(index, color + number); //双列集合
list.add(index++); //单列集合, 记录牌的编号
}
}
//添加大小王
pokers.put(index, "小王"); //双列集合
list.add(index++); //单列集合, 记录牌的编号
pokers.put(index, "大王"); //双列集合
list.add(index); //单列集合, 记录牌的编号
//2. 洗牌. 洗的是序号
Collections.shuffle(list);
//3. 发牌.
//TreeSet集合的特点: 无序, 排序, 唯一. 可以对元素进行排序,默认升序。或者用ArrayList集合,然后用Collections#sort()方法排序
TreeSet<Integer> lyf = new TreeSet<>();
TreeSet<Integer> zly = new TreeSet<>();
TreeSet<Integer> hg = new TreeSet<>();
TreeSet<Integer> dipai = new TreeSet<>();
//否则你要写: Collections.sort(List集合);
//具体的发牌规则
//发牌思路: 1. 最后三张作为底牌. 2. 牌的索引是0到53的, 依次和3取余, 余数为0给第一个人, 余数为1给第二个人, 余数为3给第三个人.
for (int i = 0; i < list.size(); i++) {
int poker = list.get(i); //就是每一张牌.
if (i >= list.size() - 3) { //51, 52, 53
dipai.add(poker);
} else if (i % 3 == 0) {
lyf.add(poker);
}else if (i % 3 == 1) {
zly.add(poker);
}else if (i % 3 == 2) {
hg.add(poker);
}
}
//4. 看牌.
//看所有人和底牌的 编号.
/*System.out.println(lyf);
System.out.println(zly);
System.out.println(hg);
System.out.println(dipai);*/
//看具体的牌
lookPoker("刘亦菲", lyf, pokers);
lookPoker("赵丽颖", zly, pokers);
lookPoker("夯哥", hg, pokers);
lookPoker("底牌", dipai, pokers);
}
//4. 定义方法, 用来看牌
/**
* 根据玩家手中的编号找对应的牌.
* @param name 玩家的名字
* @param ts 玩家手中的牌的编号
* @param pokers 双列集合, 记录的是 牌的编号和牌的关系, 规则: 编号越小, 牌越小.
*/
public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> pokers) {
System.out.print(name + "的牌是: ");
for (int pokerNumber : ts) {
System.out.print(pokers.get(pokerNumber) + " ");
}
System.out.println();
}
}
集合-部分方法总结
集合的使用步骤:
1. 创建集合对象.
2. 创建元素对象.
3. 把元素对象添加到集合中.
4. 遍历集合.
集合的遍历方式:
Collection集合: //Collection, List, Set都有的.
1. 增强for
2. 普通迭代器
3. 转数组遍历. //凑数的, 了解即可.
List体系独有的:
1. 列表迭代器.
2. 普通for循环.
Map体系:
1. 根据键找值. //普通迭代器, 增强for
2. 根据键值对获取键和值. //普通迭代器, 增强for
Collection接口中的成员方法: //仓库
add()
remove()
clear()
contains()
isEmpty()
size()
public Iterator iterator(); //根据集合对象, 获取其对应的迭代器对象. 根据仓库获取其对应的仓库管理员.
Iterator接口中的方法: //仓库管理员.
public boolean hasNext();
public E next();
ListIterator接口中的成员方法: //列表迭代器, 能解决并发修改异常.
public boolean hasNext();
public E next();
public boolean hasPrevious();
public E previous();
List接口中的独有的成员方法:
add()
remove()
set()
get() //这四个方法都是带索引的, 索引不存在会报 IndexOutOfBoundsException
LinkedList集合中独有的成员方法
//该类中的成员方法大多都是操作 集合的首尾元素的.
addFirst();
addLast();
removeFirst();
removeLast();
getFirst();
getLast();
Map接口中的成员方法:
基本的成员方法:
put()
remove()
clear()
containsKey()
containsValue()
isEmpty()
size()
获取功能的成员方法:
keySet()
values()
get()
entrySet()
Collections工具类的方法
shuffle()
sort()
reverse()
max()
min()
sychronizedXxx() //线程不安全的类 => 线程安全的类
数据结构:
栈: 先进后出.
队列: 先进先出.
数组: 查询(修改)快, 增删相对较慢.
链表: 查询(修改)相对较慢, 增删快.
哈希表: 数组 + 链表, 增删改查都快.
集合的体系图:
Collection集合: //单列集合的顶层接口
List体系:
特点:
有序, 可重复, 元素有索引.
常用子类:
ArrayList: 底层数据结构采用的是数组, 所以查询修改相对较快, 增删相对较慢.
LinkedList: 底层数据结构采用的是链表, 所以查询修改相对较慢, 增删相对较快.
Set体系:
特点:
无序, 唯一, 元素无索引.
常用子类:
HashSet: 底层数据结构采用的是哈希表, 增删改查都快, 线程不安全, 效率高.
TreeSet: 底层采用的是二叉树结构, 可以对元素排序, 但是实际开发中, 排序操作我们会放到SQL中中, 所以它了解即可.
Map集合: //双列集合的顶层接口
特点:
存储的键值对元素, 键具有唯一性, 值可以重复, 数据结构只针对于键有效.
常用子类:
HashMap: 底层数据结构采用的是哈希表, 增删改查都快, 线程不安全, 效率高.
TreeMap:
什么时候使用哪种集合?
看需求, 看用单列还是双列.
单列:
看需求, 看是否要求唯一.
是: Set
看需求, 看是否要对元素排序.
是: TreeSet
否: HashSet //需求不明确, 用HashSet
否: List
看需求, 看查询多还是增删多
查询多: ArrayList //需求不明确, 用 ArrayList
增删多: LinkedList
双列:
看需求, 看是否要对 键 排序
是: TreeMap
否: HashMap //需求不明确, 用HashMap
集合其他相关知识:
1. 匿名内部类.
格式, 写法, 应用场景.
2. LinkedHashSet集合:
有序, 唯一, 数据结构: 链表 + 哈希表
3. 哈希值问题:
同一对象哈希值肯定相同, 不同对象哈希值一般不同(例如: 重地和通话, 儿女和农丰)
4. 可变参数
本质, 位置, 个数, 用法.
5. Collections工具类
shuffle(), sort(), reverse(), max(), min()
案例:
1. ArrayList存储 字符串, 学生对象, 并遍历.
//4种方式.
2. HashSet 存储 字符串, 学生对象, 并遍历.
//2种方式, 记得保证元素唯一性.
3. HashMap案例:
A. HashMap<String, Student>
B. HashMap<Student, String>
C. ArrayList<HashMap<String, String>>
D. HashMap<String, ArrayList<String>>
E. 统计每个字符出现的次数.
4. 模拟斗地主发牌.
clear()
contains()
isEmpty()
size()
public Iterator iterator(); //根据集合对象, 获取其对应的迭代器对象. 根据仓库获取其对应的仓库管理员.
Iterator接口中的方法: //仓库管理员.
public boolean hasNext();
public E next();
ListIterator接口中的成员方法: //列表迭代器, 能解决并发修改异常.
public boolean hasNext();
public E next();
public boolean hasPrevious();
public E previous();
List接口中的独有的成员方法:
add()
remove()
set()
get() //这四个方法都是带索引的, 索引不存在会报 IndexOutOfBoundsException
LinkedList集合中独有的成员方法
//该类中的成员方法大多都是操作 集合的首尾元素的.
addFirst();
addLast();
removeFirst();
removeLast();
getFirst();
getLast();
Map接口中的成员方法:
基本的成员方法:
put()
remove()
clear()
containsKey()
containsValue()
isEmpty()
size()
获取功能的成员方法:
keySet()
values()
get()
entrySet()
Collections工具类的方法
shuffle()
sort()
reverse()
max()
min()
sychronizedXxx() //线程不安全的类 => 线程安全的类
数据结构:
栈: 先进后出.
队列: 先进先出.
数组: 查询(修改)快, 增删相对较慢.
链表: 查询(修改)相对较慢, 增删快.
哈希表: 数组 + 链表, 增删改查都快.
集合的体系图:
Collection集合: //单列集合的顶层接口
List体系:
特点:
有序, 可重复, 元素有索引.
常用子类:
ArrayList: 底层数据结构采用的是数组, 所以查询修改相对较快, 增删相对较慢.
LinkedList: 底层数据结构采用的是链表, 所以查询修改相对较慢, 增删相对较快.
Set体系:
特点:
无序, 唯一, 元素无索引.
常用子类:
HashSet: 底层数据结构采用的是哈希表, 增删改查都快, 线程不安全, 效率高.
TreeSet: 底层采用的是二叉树结构, 可以对元素排序, 但是实际开发中, 排序操作我们会放到SQL中中, 所以它了解即可.
Map集合: //双列集合的顶层接口
特点:
存储的键值对元素, 键具有唯一性, 值可以重复, 数据结构只针对于键有效.
常用子类:
HashMap: 底层数据结构采用的是哈希表, 增删改查都快, 线程不安全, 效率高.
TreeMap:
什么时候使用哪种集合?
看需求, 看用单列还是双列.
单列:
看需求, 看是否要求唯一.
是: Set
看需求, 看是否要对元素排序.
是: TreeSet
否: HashSet //需求不明确, 用HashSet
否: List
看需求, 看查询多还是增删多
查询多: ArrayList //需求不明确, 用 ArrayList
增删多: LinkedList
双列:
看需求, 看是否要对 键 排序
是: TreeMap
否: HashMap //需求不明确, 用HashMap
集合其他相关知识:
1. 匿名内部类.
格式, 写法, 应用场景.
2. LinkedHashSet集合:
有序, 唯一, 数据结构: 链表 + 哈希表
3. 哈希值问题:
同一对象哈希值肯定相同, 不同对象哈希值一般不同(例如: 重地和通话, 儿女和农丰)
4. 可变参数
本质, 位置, 个数, 用法.
5. Collections工具类
shuffle(), sort(), reverse(), max(), min()
案例:
1. ArrayList存储 字符串, 学生对象, 并遍历.
//4种方式.
2. HashSet 存储 字符串, 学生对象, 并遍历.
//2种方式, 记得保证元素唯一性.
3. HashMap案例:
A. HashMap<String, Student>
B. HashMap<Student, String>
C. ArrayList<HashMap<String, String>>
D. HashMap<String, ArrayList>
E. 统计每个字符出现的次数.
4. 模拟斗地主发牌.