[TOC]
复习
插件式编程后期项目开发使用很多,通过接口拼接其他功能模块方式可以增强代码的复用性,可移植性,同时降低代码的耦合度
1. 规范的方法
2. 接口用于方法的参数,或者作为项目模块之间的串联
3. 利用实现类完成接口中要求的方法,同时个性化定制。
List 集合
ArrayList
底层数据存储结构为 Object 类型数据,所有可以添加到底层数组数据的方法,都有对应的泛型约束,保证数据类型支持多样化的同时,满足数据类型一致化(add addALll set), 同时获取集合底层数组数据的方法返回值类型也有对应的泛型约束,满足数据类型一致化(remove(int) set get)
底层 Object 类型数组依赖于 grow 方法可以满足容量扩容操作,每次扩容的容量大约是原容量的 1.5 倍左右,
如果扩容 1.5 倍无法满足当前天啊及需求,就按照当前添加操作所需的最小容量来进行新数组的创建和扩容操作
ArrayList 构造方法
ArrayList(); 底层 Object 数组默认容量为 10 DEFAULT_CAPACITY
ArrayList(int initCapacity);
性能:
增删慢
1. 增加操作有可能触发底层数组扩容,扩容操作对于【时间效率低】【空间效率较低】
2. 除增加操作在数组的末尾【尾插法】对于性能影响不大,如果数组指定下标位置添加元素,数组中的元素内容整体向后移动,移动过程【时间效率低】
3. 删除操作在底层数组的末尾进行删除操作,性能ok,但是一旦删除数组中指定下标操作,数组中的元素整体向前移动,移动过程【时间效率较低】
4. 删除操作有可能会导致数组有效元素个数和纵容的比例降低,同时有可能触发空闲容量超出【阈值】有可能会触发【缩容操作】
查询快
底层为数组结构,数据存储空间连续,且可以根据下标 + 数组首地址方式快速计算元素所在空间位置,CPU 快速寻址,效率极高
LinkedList
带有链表头的双向链表结构,
class LinkedList {
Node<E> first;
Node<E> last;
int size;
}
class Node<E> {
Node<E> prev;
E e;
Node<E> next;
}
性能:
增删快
增加和删除操作结点。有且只是对结点直接的引用进行赋值操作,调整结点中 next 和 prev 指向关系,不涉及数据移动和底层数组扩容
查询慢
如果操作 LinkedList 中间的结点元素,我们需要从头结点或者尾结点开始挨个跳转找到目标元素,效率极低,但是操作头结点和尾结点的效率极高
addFirst(E e); addLast(E e);
E getFirst(); E getLast();
E removeFist(); E removeLast();
尾插法!!!!!!!!!!!!!!!!!!!
Object 类和 Set 集合
1. Object 类
1.1 Object 类概述
Java 中所有类的基类,Java中任何一个类都是 Object 类子类或者简介子类,Object 类内提供的方法大家都可以使用,目前需要了解的三类方法
1. 【比较相关】
boolean equals(Object obj);
判断参数对象和调用方法对象是否为同一个对象
int hashCode();
获取当前对象的系统唯一识别号,哈希值
2. 线程相关
wait();
wait(int ms);
notify();
notifyAll();
3. 数据字符串描述相关
String toString();
提供当前对象的字符串信息描述
1.2 判断需求分析
package com.qfedu.a_object;
public class Demo1 {
public static void main(String[] args) {
Student stu1 = new Student(1, "张三", 16);
Student stu2 = new Student(1, "张三", 16);
System.out.println(stu1 == stu2);
/*
* equals 方法原生情况下比较的是两个对象的地址
*
* 目前 Student 对象 stu1 和 stu2 每一个都是 new + 构造方法
*
* 实例化对应对象。占用的内存空间不一致,从内存角度分析不是同一个对象
*
*
* 需求
* 重写 equals 方法,完成对象内容比较判断是否为同一个对对象的方法
*
*/
System.out.println(stu1.equals(stu2));
System.out.println();
String str1 = "郑州大风";
String str2 = new String("郑州大风");
System.out.println(str1 == str2);
/*
* 字符串的 equals 是重写了 Object 类内的 equals 方法,
* 将字符串的比较方式从原本的地址判断是否为同一个字符串对象,转换为判断字符串数据内容是否一致
*
*/
System.out.println(str1.equals(str2));
}
}
1.3 重写 equals 方法的思路
1. 判断调用方法对象和参数对象是否为同一个【同地址】对象
如果参数对象和调用方法对象,通过最基本的 == 判断得到结果为 true, 表示方法中参与运行的两个对象时【同地址】对象,100% 为同一个对象。【直接 return true】
2. 判断调用方法对象和参数对象是否为同类型对象。
因为参数类型为 Object 类型,相当于实际类型可以是 Java 中的任何一个类型
如果调用方法对象和参数数据类型不一致,有比较的必要吗?
类型判断,如果两个对象不是同一个类型无法比较
例如;
调用方法对象:金毛类对象 参数对象:金毛类对象 【允许,同一个类型】
调用方法对象:狗类对象 参数对象:金毛类对象 【不允许,不同一个对象】
调用方法对象:金毛类对象 参数对象:狗类对象 【不允许,不是同一个对象】
如果判断不是一个类型对象,【直接 return false】
3.进行对象数据的内容判断
例如:
调用方法对象时 STudent 对象,参数对象也是 Student 对象,不是同一个地址
方法声明:
boolean equals(Object obj);
stu1.equals(stu2);
请问:
stu2 实际参数传入到方法体中,请问对外的数据类型是什么???
Object obj 参数变量赋值为 stu2
Object obj = stu2;
方法中参与运行的变量是 obj 数据类型为 Object 类型。【多态】
需求:
Object 类型无法获取 Student 类型相应数据和方法,完成【数据类型转换】
Student temp = (Student) obj;
数据类型转换是否存在隐患,重点关注数据的真实类型,通过该 equals 方法前两步判断,我们可以得出参数类型和当前调用方法类型为同一个类型,经过验证/判断【强转】
package com.qfedu.a_object;
import java.util.Objects;
/*
* Alt + Ctrl + S 可以生成构造方法, Setter and getter 方法,toString...
*/
public class Student {
private int id;
private String name;
private int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
/*
* 【补充知识点】
* this 关键字表示调用方法的对象本身,或者是 new + 构造方法 实例化对象本身
*
* 目前哪一个对象调用方法,哪一个是对象时 this
*/
@Override
public boolean equals(Object obj) {
// 1. 判断调用方法对象和参数对象是否为同一个【同地址】对象
if (this == obj) {
return true;
}
/*
* 2. 判断调用方法对象和参数对象是否为同类型对象
* 对象 instanceof 类型 ==> 判断对象是否为当前类型
*
* 判断 obj 到底是不是 Student 类型,关注的是 obj 对应的真实类型
*/
if (obj instanceof Student == false) {
// !(obj instanceof Student)
return false;
}
// 3. 进行对象数据内容判断
// 3.1 强制类型转换
Student temp = (Student) obj;
/*
* 3.2 比较参数对象和调用方法对象内容是否一致
* int id ==
* String name 字符串判断是否一致,使用 equals 方法
* 【注意】 开发后期会遇到大量的同名方法,需要明确方法的执行者到底是谁
*
* int age ==
*
* id == temp.id
* 1. 为什么可以直接用??
* 因为在 Student 类内
* 2. id => 谁的??
* 没有加任何修饰和限制的 id 是当前调用方法对象的 id
*
*/
return id == temp.id
&& name.equals(temp.name)
&& age == temp.age;
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
}
1.4 hashCode 重写要求【不讲道理】
【规定】
Java 中要求如果两个独享通过 equals 方法判断结果为 true ,证明两个对象为同一个对象,要求两个对象的hashCode 方法得到的哈希值必须一致
hashCode 方法在没有重写的情况,,默认返回值数据是当前对象在内存占用地址的【十进制数据】,该数据具备唯一性,可以作为身份标识.
需要按照 equals 方法结果判断为 true, 重写 hashCode 方法
方法声明
int hashCode();
重写逻辑:
所有参与 equals 判断操作的成员变量,全部用于生成哈希值
请外挂:
Object Object 工具类
int hash(Object... valuse);
可以根据提供数据情况,自动生成 哈希值
Object... 不定长参数
要求提供的数据类型必须是 Object 类型,等价于 Java 中任意数据类型都 ok
... 表示实际参数个数不限制,可以是多个,可以是一个,可以是没有。
【重点】 不定长参数在方法内是一个数组
Object... ==> 方法体中是 Object[]
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
2. Set 集合
2.1 Set 集合概述
Set 集合使用的方法都是来自于 Collection 接口,没有特征方法。
Set 集合特征:
1. 无序
2. 不可重复
Set 集合两个实现类:
class HashSet
底层结构为 哈希表结构,要求添加数据必须有【比较方式】 保证数据唯一
class TreeSet
底层结构为 二叉树结构,要求添加的数据必须有【自然顺序】或者【比较方式】
package com.qfedu.b_Set;
import java.util.HashSet;
/*
* HashSet 存储自定有元素分析 HashSet 存储流程和相关方法
*
* Set 集合要求【数据不可重复】
*/
public class Demo2 {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<Person>();
/*
* 添加元素 6 个,发现调用了 6 个 hashCode 方法
*
* 调用添加元素的 hashCode 方法,主要用于计算当前元素应该存储在哈希表的哪一个单元格位置
*
*/
set.add(new Person(1, "牛二", 18));
set.add(new Person(2, "牛三", 28));
set.add(new Person(3, "牛四", 48));
set.add(new Person(4, "牛五", 38));
set.add(new Person(5, "牛六", 28));
set.add(new Person(6, "牛八", 18));
System.out.println();
/*
* 再次添加元素,目前添加元素,按照重写 equals 和 hashCode 方法判断
* 和之前的添加元素存在相同的情况。
*
* 添加元素调用 hashCode 计算对应的单元格位置,发现对应单元格已经存在其他元素。
* 调用 equals 方法比较,如果两个元素判断结果为 true,为相同元素,后者无法添加
* 如果 equals 判断两个元素为 false, 可以添加【避免】
*
*/
boolean ret1 = set.add(new Person(3, "牛四", 48));
System.out.println(ret1);
boolean ret2 = set.add(new Person(7, "牛⑦", 68));
System.out.println(ret2);
System.out.println("size: " + set.size());
}
}
2.2 HashSet 数据存储流程分析
底层结构为 哈希表结构,哈希表结构存储效率极高,需要添加的元素提供【哈希值】,用于计算当前元素应该存储到哈希表的哪一个单元格位置
添加的元素提供【哈希值】 ==> 添加的元素需要调用 hashCode 方法获取哈希值
哈希表中的每一个单元格,都是一个【桶式结构】,数据存储允许多个,且数据没有顺序,但是为了方便使用和管理,期望哈希表每一个单元格有且只有一个元素,Set集合存储数据要求。
package com.qfedu.b_Set;
import java.util.HashSet;
/*
* HashSet 存储自定有元素分析 HashSet 存储流程和相关方法
*
* Set 集合要求【数据不可重复】
*/
public class Demo2 {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<Person>();
/*
* 添加元素 6 个,发现调用了 6 个 hashCode 方法
*
* 调用添加元素的 hashCode 方法,主要用于计算当前元素应该存储在哈希表的哪一个单元格位置
*
*/
set.add(new Person(1, "牛二", 18));
set.add(new Person(2, "牛三", 28));
set.add(new Person(3, "牛四", 48));
set.add(new Person(4, "牛五", 38));
set.add(new Person(5, "牛六", 28));
set.add(new Person(6, "牛八", 18));
System.out.println();
/*
* 再次添加元素,目前添加元素,按照重写 equals 和 hashCode 方法判断
* 和之前的添加元素存在相同的情况。
*
* 添加元素调用 hashCode 计算对应的单元格位置,发现对应单元格已经存在其他元素。
* 调用 equals 方法比较,如果两个元素判断结果为 true,为相同元素,后者无法添加
* 如果 equals 判断两个元素为 false, 可以添加【避免】
*
*/
boolean ret1 = set.add(new Person(3, "牛四", 48));
System.out.println(ret1);
boolean ret2 = set.add(new Person(6, "牛⑦", 68));
System.out.println(ret2);
System.out.println("size: " + set.size());
}
}
2.3 TreeSet 树形结构图例
![](D:\AQF\teacher\Day22-Object 类和 Set集合\img\01-树形结构说明.png)
2.4 TreeSet 存储自定义元素
【方案一】
自定义类遵从 Comparable ,增强自定义类,变成可比较类型
package com.qfedu.b_Set;
/*
* 自定义类,遵从 Comparable 接口,接口泛型约束为 SingleDog 类型,类型自身比较
*
* 实现 int compareTo(SingleDog o)
*
* 【可比较的类】 类型增强A
*/
public class SingleDog implements Comparable<SingleDog> {
private int id;
private String name;
private int age;
// 根据所需完成 Constructor ,Setter and Getter
public SingleDog() {
}
public SingleDog(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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 "SingleDog [id=" + id + ", name=" + name + ", age=" + age + "]";
}
/*
* 要求实现的比较方式,该方法返回值为 int 类型,可以用于 TreeSet 结构判断元素的大小关系
* 返回值可以分为两种情况
* 1. 负数或者正数,两个元素存在大小关系,至于升序还是降序,不重要
* 2. 0 证明两个元素一致,按照 Set 集合要求无法存储相同的元素
*
*/
@Override
public int compareTo(SingleDog o) {
System.out.println("SingleDog 类遵从 Comparable 接口实现 compareTo 方法,完成排序比较方式");
// 按照年龄进行比较大小操作
return age - o.age;
}
}
package com.qfedu.b_Set;
import java.util.TreeSet;
public class Demo4 {
public static void main(String[] args) {
/*
* TreeSet 结构存储 SingLeDog 类型, SingleDog 类型遵从 Comparable 接口
* 是一个可比较的类型,TreeSet 允许存储
*
* TreeSet 接口会自动执行 SingleDog 遵从 Comparable 接口实现的 compareTo 方法
* 从而满足当前结构存储顺序需求
*
*/
TreeSet<SingleDog> set = new TreeSet<SingleDog>();
set.add(new SingleDog(1, "张三", 16));
set.add(new SingleDog(2, "李四", 26));
set.add(new SingleDog(3, "王五", 6));
set.add(new SingleDog(4, "赵六", 76));
set.add(new SingleDog(5, "刘某", 56));
set.add(new SingleDog(6, "王某", 36));
System.out.println(set);
}
}
【方案二】
TreeSet 插件式增强,提供比较排序存储元素的比较器,比较器遵从 Comparator 接口
package com.qfedu.b_Set;
import java.util.Comparator;
import java.util.TreeSet;
/*
* 使用 Comparator 接口实现类对象增强 TreeSet
*
* 自定义比较器遵从 Comparator 接口,要求实现 compare方法
* int compare(T o1, T o2)
*
*/
class PersonComparator implements Comparator<Person> {
/*
* 要求实现的比较方式,该方法返回值为 int 类型,可以用于 TreeSet 结构判断元素的大小关系。
* 返回值可以分为两种情况
* 1. 负数 或者 正数,两个元素存在大小关系,至于升序还是降序,不重要
* 2. 0 证明两个元素一致,按照 Set 集合要求无法存储相同元素
*/
@Override
public int compare(Person arg0, Person arg1) {
System.out.println("自定义比较器遵从 Comparator 接口,实现 compare 方法");
return arg0.getAge() - arg1.getAge();
}
}
public class Demo5 {
public static void main(String[] args) {
/*
* TreeSet 树形 Set 集合,存储 Person 类型,通过构造方法参数提供对应的比较方式
*
* Comparator 接口实现类中的 compare 方法由 Treeset 结构自行调用
*/
// TreeSet<Person> set = new TreeSet<Person>(new PersonComparator());
/*
* 可以优化引入 Lambda !
*
* 引入 Lambda 表达式可以降低类型需求,内存需求,代码执行时间效率更高
*/
TreeSet<Person> set = new TreeSet<Person>((o1, o2) -> o2.getAge() - o1.getAge());
set.add(new Person(1, "牛二", 18));
set.add(new Person(2, "牛三", 28));
set.add(new Person(3, "牛四", 48));
set.add(new Person(4, "牛五", 38));
set.add(new Person(5, "牛六", 28));
set.add(new Per6, "牛八", 18));
System.out.println(set);
}
}
@所有人
1. API 抄写
2. 项目代码增加一个需求:
根据条件过滤展示学生信息,要求使用接口形式过滤判断
3. 复习
4. 目前所学关键字整理
5. 下周一开始 8:30 晨考