Day04-面向对象和Java基础类库
一、单列集合
1:概念
集合是用来存储多个同类型数据的容器, 它的长度是可以变化的.
2:集合的体系图[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CADiAnY5-1670500848182)(…/img/集合.png)]
记忆
集合的顶层都是接口, 其中Collection接口是单列集合的顶层接口, Map接口是双列集合的顶层接口.
Collection接口(单列集合)有两大子体系:
-
List体系的特点是: 有序, 可重复.
-
Set体系的特点是: 无序, 唯一.
一个小技巧: 以后但凡我们学习一个新的体系时, 都建议采用学顶层, 用底层的方式来学习.
解释: 因为顶层封装的是整个继承体系的共性内容, 而底层才是具体的实现, 体现.
3:Collection集合
3-1:概述
Collection集合是单例集合的顶层接口, 里边定义了所有单列集合都共有的内容.
3-2:创建Collection对象
因为Collection是接口, 所以不能直接通过new关键字来创建它的对象, 那它如何创建对象呢?
我们可以通过多态的方式, 创建其子类对象, 从而实现创建Collection接口对象. 代码如下:
Collection list = new ArrayList();
解释:
集合后边的<数据类型>是泛型的意思, 泛型是用来限定集合中存储元素的数据类型的.
例如:
- Collection说明集合中只能存储字符串. 泛型(起到了限制的作用)
- Collection说明集合中只能存储整数. 泛型(起到了限制的作用)
泛型只能是引用数据类型, 且在实际开发中, 泛型一般只结合集合来一起使用.
3-3:案例一
需求:
创建Collection集合对象, 用来存储字符串.
调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:
"hello", "world", "java"
通过输出语句, 直接打印集合对象, 并观察结果.
public class CollectionDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList();
c.add("hello");
c.add("world");
c.add("java");
//打印集合对象
System.out.println(c);
}
}
3-4:Collection集合的成员方法
1.public boolean add(E e) 添加元素.
2.public int size() 获取集合的长度, 即集合中元素的个数
3.public boolean remove(Object obj) 从集合中移除指定的元素.
4.public boolean contains(Object obj) 判断集合中是否包含指定的元素
5.public void clear() 清空集合对象
6.public boolean isEmpty() 判断集合是否为空
3-5:案例一
通过多态的形式, 创建Collection集合对象.
调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:
"hello", "world", "java"
分别测试上述的6个成员方法.
public class Test1 {
public static void main(String[] args) {
//1.public boolean add(E e) 添加元素.
Collection<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
System.out.println("初始集合:"+list);
//2.public int size() 获取集合的长度, 即集合中元素的个数
int s = list.size();
System.out.println("长度:"+s);
//3.public boolean remove(Object obj) 从集合中移除指定的元素.
list.remove("world");
System.out.println("删除world后:"+list);
//4.public boolean contains(Object obj) 判断集合中是否包含指定的元素
boolean b = list.contains("world");
System.out.println("此时world是否存在:"+b);
//5.public void clear() 清空集合对象
list.clear();
System.out.println("集合清空后:"+list);
//6.public boolean isEmpty() 判断集合是否为空
boolean e = list.isEmpty();
System.out.println("集合是否为空:"+e);
}
}
3-6:案例二
需求
通过多态的形式, 创建Collection集合对象.
调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:
“hello”, “world”, “java”
通过迭代器遍历集合, 获取到每一个元素, 并打印到控制台上.
提示:
1.Collection集合中的方法
2.public Iterator iterator() //根据集合对象, 获取其对应的迭代器对象.
3.解释:
a)因为Iterator是接口, 所以这里返回的其实是Iterator接口的子类对象.
b)迭代器是依赖于集合而存在的.
4.Iterator迭代器中的方法
public boolean hasNext() //判断迭代器中是否还有下一个元素.
public E next() //获取迭代器中的下一个元素.
public class Test2 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
//通过迭代器遍历集合
Iterator<String> it = list.iterator();
//判断迭代器中是否还有下一个元素.
while(it.hasNext()){
//有就获取迭代器中的下一个元素
String s = it.next();
System.out.println(s);
}
}
}
3-7:案例三
需求
定义一个学生类, 属性为姓名和年龄.
创建Collection集合, 用来存储学生对象.
往Collection集合中, 添加3个学生的信息.
通过迭代器, 遍历集合.
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 +
'}';
}
}
public class Test3 {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 28);
Student s3 = new Student("王五", 38);
//创建集合对象
Collection<Student> list = new ArrayList<>();
//把学生对象放到集合中
list.add(s1);
list.add(s2);
list.add(s3);
//遍历集合
Iterator<Student> it = list.iterator();
while(it.hasNext()){
Student s = it.next();
System.out.println(s);
}
}
}
3-8 总结
集合的步骤可以分为四大步三小步, 具体如下:
创建集合对象.
创建元素对象.
把元素添加到集合中.
遍历集合.
根据集合对象获取其对应的迭代器对象.
通过Collection#iterator()方法实现.
判断迭代器中是否有下一个元素.
通过Iterator#hasNext()方法实现.
如果有, 就获取该元素.
通过Iterator#next()方法实现.
4:List集合
4-1:概述
有序集合(也称为序列), 该界面的用户可以精确控制列表中每个元素的插入位置。且用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。与Collection集合不同,列表通常允许重复的元素。
简单记忆:
List集合的元素特点是: 有序, 可重复, 元素有索引.
- 有序: 指的是元素的存储顺序和取出顺序是一致的.
- 可重复: 指的是List集合可以存储重复的元素.
- 元素有索引: 指的是List集合中每个元素都是由索引的, 且索引是从0开始的.
4-2:List集合特有的成员方法.
public void add(int index, E element)
解释: 在集合的指定位置(索引), 插入指定的元素, 索引越界会报IndexOutOfBoundsException异常.
public E remove(int index)
解释: 删除指定索引处的元素, 并返回被删除的元素, 索引越界会报IndexOutOfBoundsException异常.
public E set(int index, E element)
解释: 修改指定索引处的元素为指定的值, 并返回修改前的元素, 索引越界会报IndexOutOfBoundsException异常.
public E get(int index)
解释: 根据索引, 获取其对应的元素, 索引越界会报IndexOutOfBoundsException异常.
4-3:存储自定义对象并遍历
需求
定义一个学生类, 属性为姓名和年龄.
创建List集合, 用来存储学生对象.
往List集合中, 添加3个学生的信息.
用集合的特有成员方法, 来遍历List集合.
public class Test4 {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 28);
Student s3 = new Student("王五", 38);
//创建集合对象
ArrayList<Student> list = new ArrayList<>();
//把学生对象放到集合中
list.add(s1);
list.add(s2);
list.add(s3);
//遍历集合
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
}
}
5:增强for
5-1:概述
增强for是JDK1.5的新特性, 它是用来简化数组和Collection集合的遍历的.
5-2:格式
for(元素的数据类型 变量名 : 数组或者Collection集合对象) {
//上述的 变量名 代表的就是, 数组或者Collection集合中的每一个元素.
}
5-3:好处
增强for是用来简化数组和Collection集合的遍历的.
5-4:注意事项
要通过增强for遍历的数组或者Collection集合, 不能为null.
即: 增强for的目标要判断是否为null.
5-5:案例一:集合遍历(ArrayList)
需求
定义学生类, 属性为姓名和年龄.
创建List集合, 用来存储学生对象.
往集合中添加3个学生的信息.
通过增强for遍历上述的List集合.
public class Test4 {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 28);
Student s3 = new Student("王五", 38);
//创建集合对象
ArrayList<Student> list = new ArrayList<>();
//把学生对象放到集合中
list.add(s1);
list.add(s2);
list.add(s3);
//遍历集合方式1
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
//遍历集合方式2
for (Student s : list) {
System.out.println(s);
}
}
}
5-6:案例二:遍历数组
需求
定义int类型的数组, 存储元素1, 2, 3, 4, 5.
通过增强for, 遍历上述的数组.
public class ListForDemo {
public static void main(String[] args) {
//创建数组 arr
int[] arr = {1,2,3,4,5};
//增强 for 循环打印输出
for (int i : arr) {
System.out.println(i);
}
}
}
6:常见的数据结构
6-1:概述
数据结构是计算机存储, 组织数据的方式. 它是指相互之间存在一种或多种特定关系的数据元素的集合. 通常情况下, 精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关.
大白话理解:
数据结构指的就是数据的组织方式.
6-2:常用的数据结构
6-3:栈和队列
栈结构:数据先进后出模型
队列结构:数据先进先出模型
思考
我要把A, B, C三个元素存入到栈中, 流程是怎样的呢?
我要把A, B, C三个元素存入到队列中, 流程是怎样的呢?
6-4:数组和列表
数组结构 特点:查询快、增删慢
链表结构 特点:查询慢、增删快
7:List集合的子类
7-1:常用子类
List集合是一个接口, 它的常用子类有两个, 分别是除了ArrayList, 还有LinkedList.
-
ArrayList集合的特点: 底层数据结构是数组, 查询和修改快, 增删慢.
-
LinkedList集合的特点: 底层数据结构是链表, 查询和修改慢, 增删快.
注意: 它们的相同点是, 都是有序的, 而且可以存储重复元素.
7-2 案例一:存储学生并遍历(LinkedList)
需求
定义学生类, 属性为姓名和年龄.
创建LinkedList集合, 用来存储学生信息.
往集合中添加3个学生对象.
遍历上述的集合.
public class Test5 {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 28);
Student s3 = new Student("王五", 38);
//创建集合对象
LinkedList<Student> list = new LinkedList<>();
//把学生对象放到集合中
list.add(s1);
list.add(s2);
list.add(s3);
//遍历集合方式1
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
//遍历集合方式2
for (Student s : list) {
System.out.println(s);
}
}
}
8:Set集合
8-1:概述
Set集合是Collection集合的子体系, 它的元素特点是无序, 唯一.
Set集合是一个接口, 所以不能通过new的方式直接创建它的对象.
Set集合中没有带索引的方法, 所以不能通过普通for循环遍历.
Set集合的常用子类主要有两个, 分别是HashSet集合和TreeSet集合
8-2:哈希值
8-2-1 :概述
所谓的哈希值指的是JDK根据对象的属性, 或者字符串, 或者数字 算出来的int类型的数值.
8-2-2:如何获取哈希值
可以通过Object#hashCode()方法, 获取指定对象的哈希值, 具体如下:
public int hashCode(); //根据对象, 获取其对应的哈希值.
案例一:哈希值入门
需求
-
定义学生类, 属性为姓名和年龄.
-
在测试类的main方法中, 创建两个学生对象, 分别获取它们的哈希值, 并打印.
-
测试: 重写Object#hashCode()方法, 实现不同对象的哈希值也是相同的.
-
测试: 同一对象哈希值肯定相同, 不同对象哈希值一般不同.
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;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("张三", 18);
Student s2 = new Student("张三", 18);
//如果Student类中不重写hashCode(),两个对象值不同
//如果Student类中重写了hashCode(),两个对象值相同
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
}
}
结论
1.同一个对象多次调用hashCode()方法, 返回的哈希值是相同的.
2.默认情况, 不同对象的哈希值是不同的. 但是通过方法重写, 可以实现不同对象的哈希值相同.
3.同一对象哈希值肯定相同, 不同对象哈希值一般不同.
9:HashSet集合
9-1:特点
-
底层数据结构是哈希表.
-
对集合的迭代顺序不做任何保证, 也就是说不保证元素的存取顺序一致.
-
没有带索引的方法, 所以不能通过普通for循环遍历.
-
由于是Set集合, 所以是不包含重复元素的集合.
总结:
HashSet集合的特点是: 无序, 唯一, 元素无索引, 它的底层数据结构是: 哈希表.
案例一:存储学生并遍历
需求
-
定义学生类, 属性为姓名和年龄.
-
创建HashSet集合, 用来存储2个学生对象.
-
遍历集合, 并把结果打印到控制台上.
要求: 如果学生对象的各个属性值都相同, 我们就认为它们是同一个对象.
public class Test {
public static void main(String[] args) {
Student s1 = new Student("张三", 18);
Student s2 = new Student("张三", 18);
//如果Student类中不重写hashCode(),两个对象值不同
//如果Student类中重写了hashCode(),两个对象值相同
//System.out.println(s1.hashCode()==s2.hashCode());
//创建HashSet集合
HashSet<Student> set = new HashSet<>();
//把学生放到集合中
set.add(s1);
set.add(s2);
System.out.println(set);
}
}
.细节: 在Student类中重写**hashCode()和equals()**方法, 即可保证元素的唯一性.
案例二:存储字符串并遍历
需求
-
定义HashSet集合, 存储字符串"hello", “world”, “java”, “world”
-
遍历HashSet集合, 打印每一个元素值, 并观察程序的运行结果.
public class Test1 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
//Set中添加元素
set.add("hello");
set.add("world");
set.add("java");
set.add("world");
//循环输出每个元素
System.out.println(set);
}
}
9-2:常见数据结构之哈希表
• JDK8以前, 底层采用数组 + 链表的形式实现, 可以理解为: 一个元素为链表的数组.
• JDK8以后, 在长度比较长的时候, 底层实现了优化.
图解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m9BeXwzz-1670500848183)(…/img/链表+数组.png)]
解释:
-
"hello"的哈希值是99162322
-
"world"的哈希值是113318802
-
"java"的哈希值是3254818
-
"world"的哈希值是113318802
-
"通话"的哈希值是1179395
-
"重地"的哈希值是1179395
-
存储的时候, 会将上述的哈希值和16进行取余操作, 然后根据余数值进行存储.
二、双列集合
1:概念
Map集合是双列集合的顶层接口, 它是用来存储键值对对象的,
Map集合的特点:
双列集合,一个键对应一个值
键不可以重复,值可以重复
2:格式
interface Map<K,V> K:键的类型;V:值的类型
例如: “heima001”,”张三” “heima002”,”李四”
3:创建对象
因为Map是接口, 不能通过new关键字直接创建它的对象, 我们可以通过多态的形式, 创建其子类对象. 从而实现创建Map集合对象的这个需求.
注意: Map集合的常用子类主要有两个, 分别是: HashMap****和TreeMap.
3-1:Map入门
需求
-
定义Map集合, 键是学号, 值是学生的名字. (键值都是字符串类型).
-
往Map集合中添加3对元素.
-
打印Map集合对象.
public class Demo01 {
public static void main(String[] args) {
//1. 创建集合对象. 键是学号, 值是学生的名字
Map<String, String> hm = new HashMap<>();
//2. 创建元素对象.
//3. 把元素对象添加到集合中.
hm.put("itheima001","刘亦菲");
hm.put("itheima002","赵丽颖");
hm.put("itheima003","高圆圆");
//4. 打印集合.
System.out.println(hm);
}
}
4:Map集合的成员方法
4-1:方法描述
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
4-2:案例
需求
-
定义Map集合, 键是丈夫, 值是妻子. (键值都是字符串类型).
-
分别测试上述的7个方法.
public class Test2 {
public static void main(String[] args) {
//增:put方法
HashMap<String, String> map = new HashMap<>();
map.put("杨过","小龙女");
map.put("郭靖","黄蓉");
map.put("邓超","孙俪");
//打印集合
System.out.println(map);
//长度:size
System.out.println("长度:"+map.size());
//删: remove
map.remove("邓超");
System.out.println("删除邓超后:"+map);
//是否包含key: containsKey
boolean f = map.containsKey("邓超");
System.out.println("邓超是否存在:"+f);
//是否包含value: containsValue
boolean f2 = map.containsValue("孙俪");
System.out.println("孙俪是否存在:"+f2);
}
}
5:Map集合的获取功能
5-1:方法的描述
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Collection values() | 获取所有值的集合 |
5-2:案例
案例一:测试获取功能
需求
定义Map集合, 键是丈夫, 值是妻子. (键值都是字符串类型).
先通过代码测试上述的3个方法. 即: get(), keySet(), values()
public class Test3 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("杨过","小龙女");
map.put("郭靖","黄蓉");
map.put("邓超","孙俪");
System.out.println(map);
//get: 根据key获取value
String s = map.get("邓超");
System.out.println("邓超对应的是:"+s);
//keyset: 获取所有的键
Set<String> keys = map.keySet();
System.out.println(keys);
//values: 获取所有的值
Collection<String> values = map.values();
System.out.println(values);
}
}
案例二:Map集合的遍历方式
需求
-
定义Map集合, 键是丈夫, 值是妻子. (键值都是字符串类型).
-
往集合中添加3对键值对元素.
-
遍历Map集合.
思路分析
我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合.
-
把所有的丈夫给集中起来
-
遍历丈夫的集合,获取到每一个丈夫
-
根据丈夫去找对应的妻子
//案例: 遍历Map集合. 根据键获取值.
public class Demo02 {
public static void main(String[] args) {
//1. 创建集合对象. 键是学号, 值是学生的名字
Map<String, String> hm = new HashMap<>();
//2. 创建元素对象.
//3. 把元素对象添加到集合中.
hm.put("杨过", "小龙女");
hm.put("乔峰", "阿朱");
hm.put("段誉", "王语嫣");
//4. 打印集合.
//4.1 获取集合中所有的键.
Set<String> keys = hm.keySet();
//4.2 遍历, 获取到每一个键.
for (String key : keys) {
//4.3 根据键, 获取值, 然后输出.
String value = hm.get(key);
System.out.println(key + "..." + value);
}
}
}
6:Map集合的案例
案例一:键是Student值是String
需求
-
创建HashMap集合, 键是学生对象(Student), 值是居住地(String).
-
往HashMap集合中添加3组数据.
-
通过两种方式, 遍历HashMap集合.
注意: HashMap集合想保证键的唯一性, 依赖**hashCode()和equals()**这两个方法.
public class Test4 {
public static void main(String[] args) {
//创建集合对象
HashMap<Student, String> map = new HashMap<>();
//往把学生对象和地址成对添加到集合中
map.put(new Student("张三",18),"邯郸");
map.put(new Student("李四",28),"北京");
//遍历集合
Set<Student> students = map.keySet();
for (Student k : students) {
String v = map.get(k);
System.out.println(k.getName()+","+k.getAge()+","+v);
}
}
}
案例二:统计每个字符的次数
需求
-
键盘录入一个字符串,要求统计字符串中每个字符出现的次数。
-
举例:键盘录入“aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)”
public class Test6 {
public static void main(String[] args) {
//获取键盘录入的字符串
Scanner sc = new Scanner(System.in);
System.out.println("统计字母次数:");
String str = sc.nextLine();
//创建map集合
HashMap<Character, Integer> map = new HashMap<>();
//遍历字符串
for (int i = 0; i <str.length() ; i++) {
char c = str.charAt(i);
//判断集合中是否存在当前内容
if(!map.containsKey(c)){
map.put(c,1);//不存在, 直接存储, 次数记录为1.
}else{
map.put(c,map.get(c)+1);//存在,次数+1,在存储
}
}
// System.out.println(map);//测试
Set<Character> keys = map.keySet();
for (Character k : keys) {
Integer v = map.get(k);
System.out.print(k+"("+v+")");
}
}
}
三、Collections集合工具类
3-1:概述
Collections类是针对集合操作的工具类.
3-2:常用方法
方法名 | 说明 |
---|---|
public static void sort(List list) | 将指定的列表按升序排序 |
public static void reverse(List<?> list) | 反转指定列表中元素的顺序 |
public static void shuffle(List<?> list) | 使用默认的随机源随机排列指定的列表 |
3-3:需求
-
定义ArrayList集合, 存储5个整数.
-
分别测试上述的3个方法.
public class CollectionsDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
//输入五个数字
list.add(1);
list.add(3);
list.add(2);
list.add(5);
list.add(4);
//按升序排序
Collections.sort(list);
//打印输出
System.out.println(list);
//翻转元素的顺序
Collections.reverse(list);
//打印输出
System.out.println(list);
//随机打散排列的列表
Collections.shuffle(list);
//打印输出
System.out.println(list);
}
}
四、可变参数
1:概述
如果遇到让我们定义一些方法, 分别获取2个整数, 3个整数, 4个整数的和, 此时我们只能通过方法重载的形式来实现, 但是这样做比较繁琐, 我们发现上述的这些方法, 只有参数列表是变化的, 那能不能优化这个问题呢?
答案肯定是可以的, 我们可以通过可变参数来优化这个问题.
可变参数又称参数个数可变,它用作方法的形参出现,那么方法参数个数就是可变的了
2:格式
修饰符 返回值类型 方法名(数据类型… 变量名) {
方法体;
return 返回值;
}
3:注意事项
这里的变量其实是一个数组.
大白话翻译: 可变参数的底层就是一个数组.
如果一个方法有多个参数,其中包含可变参数,可变参数要放在最后.
大白话解释: 方法的形参列表有且只能有一个可变参数, 并且可变参数要放到形参列表的最后.
4:案例
需求
-
定义getSum()方法, 用来获取n个整数的和(n可能是任意的一个数字).
-
在main方法中, 调用getSum()方法.
public class Test8 {
public static void main(String[] args) {
//调用 getSum
int result = getSum(1,2,3,4,5,6,7,8);
//打印输出
System.out.println(result);
}
public static int getSum(int... arr){
int sum = 0;
for (int i : arr) {
sum += i;
}
return sum;
}
}