零.晨考复习
1. 接口的定义规范
interface 接口名 {
成员变量 【缺省属性】public static final
成员方法 【缺省属性】public abstract
}
2. 类遵从接口的格式和关键字
class 类名 implements 接口名 {
必须实现 接口中缺省属性为 public abstract 修饰方法
}
3. 接口有哪些特殊用法
1. 类可以通过遵从多个接口,不同的接口使用 , 隔开
2. 接口可以继承,并且允许多继承
3. default 关键字在接口中修饰方法,对应的方法有方法体
4. 多态概述和用途
编译看左,运行看右
父类的引用数据类型变量指向子类对象
接口的引用数据类型变量指向遵从接口的实现类对象
5. 泛型在增强单一方法的格式和要求
权限修饰符 [static] <自定义泛型占位符> 返回值类型 方法名(形式参数列表) {
方法体;
}
【严格要求】
形式参数列表中,必须有一个参数对应的针对于自定义泛型
6. 类带有自定义泛型的情况下,泛型对应具体数据类型的约束方式
需要通过实例化对象方式约束
类名<具体数据类型> 类对象 = new 类名<>() IDEA
类名<具体数据类型> 类对象 = new 类名<具体数据类型>() Eclipse
7. static 修饰静态成员方法有什么特征
1. 推荐通过类名直接调用,不建议使用类对象调用
2. 可以直接使用类内的其他静态资源
3. 不可以使用类内的其他非静态资源
4. 通常用于工具类封装,节省实例化对象所需的空间和时间
8. final 修饰成员方法有什么要求
不能被子类重写,一般是最终方法
一.集合
集合体系
1.1集合解决的问题
针对与目前的数据情况下,存储大量数据类型一致的元素,使用的方式 ==> 【数组】
但是数组存在很多弊端
1.数组支持数据类型单一
2.数组容量不可变
3.数组对应的配套方法较少
集合就可以解决以上问题
提供了多种多样的底层构建
关注不同的性能方式
提供大量的方法配合集合使用,提高开发效率
1.2集合继承结构
interface Collection<E>
Java 中所有集合的底层接口,带有泛型 E ==> Element 元素
--| interface List<E> 有序,可重复
----| class ArrayList<E> 底层数据存储结构数组
----| class LinkedList<E> 底层数据存储结构为带有【链表头的双向链表】
----| class Vector<E> 底层数据存储结果数组,同时【线程安全,效率低】
--| interface Set<E> 无序,不可重复
----| class HashSet<E> 底层存储数据结构为哈希表结构[设想execl表格]
----| class TreeSet<E> 底层存储数据结构为平衡二叉树
1.3Collection 相关方法
增
add(E e);
添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
addAll(Collection<? extends E> c);
添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化过程中,约束泛型
对应具体数据类型,或者其他子类类型。
删
remove(Object obj);
在当前集合中,删除指定元素
removeAll(Collection<?> c);
在当前集合中,删除参数集合和当前集合的交集
retainAll(Collection<?> c);
在当前集合中,仅保留参数集合和当前集合的交集
clear();
清空当前集合中所有数据内容
查
int size();
当前集合中有效元素个数
boolean isEmpty();
判断当前集合是否为空
boolean contains(Object obj);
判断参数对象是否在当前集合中存在
boolean containsAll(Collection<?> c);
判断参数集合是否是当前集合的子集合
Object[] toArray();
返回当前集合中所有元素对象为Object类型的数组
练习:
package com.qf.list;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
import org.w3c.dom.ls.LSOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class Demo1 {
public static void main(String[] args) {
/*
Collection 是一个接口,无法实例化对象,可以使用接口的实现类对象
来演示方法情况
*/
Collection<String> c = new ArrayList<>();
c.add("晏曲烩面");
c.add("大盘鸡");
c.add("班记油泼面");
c.add("阿生哥螺蛳粉永安街总店");
c.add("洛馍村");
c.add("文化路和任寨北街刘记羊肉汤");
System.out.println(c);
Collection<String> c2 = new ArrayList<>();
c2.add("栖三餐厅");
c2.add("上品牛排");
c2.add("俄式厨房");
c.addAll(c2);
System.out.println(c);
Collection<String> c3 = new ArrayList<>();
c3.add("大盘鸡");
c3.add("班记油泼面");
c3.add("上品牛排");
c.remove("洛馍村");
System.out.println(c);
/* c.removeAll(c3);
System.out.println(c);
c.retainAll(c3);
System.out.println(c);// []
c3.clear();
System.out.println(c3);// []
*/
System.out.println(c3);// [大盘鸡, 班记油泼面, 上品牛排]
System.out.println(c.size());// 8
System.out.println(c.isEmpty());// false
System.out.println(c3.size());// 3
System.out.println(c3.isEmpty());// false
System.out.println(c.contains("洛馍村"));// false
System.out.println(c.containsAll(c3));// true
Object[] objects = c.toArray();
Arrays.stream(objects).forEach(System.out::print);// 晏曲烩面大盘鸡班记油泼面阿生哥螺蛳粉永安街总店文化路和任寨北街刘记羊肉汤栖三餐厅上品牛排俄式厨房
}
}
1.4List集合相关方法
List 集合有一个非常明显的特征: 【下标】
List 集合底层主要的结构 一种是数组,一种是双向链表
增
add(E e);
添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
addAll(Collection<? extends E> c);
添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化过程中,约束泛
型对应具体数据类型,或者其子类类型。
add(int index, E e);
在指定下标位置,添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
addAll(int index, Collection<? extends E> c);
在指定下标位置,添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化
过程中,约束泛型对应具体数据类型,或者其子类类型。
删
E remove(int index);
在当前List集合中,删除指定下标元素,返回值是被删除元素对象本身
remove(Object obj);
在当前集合中,删除指定元素
removeAll(Collection<?> c);
在当前集合中,删除参数集合和当前集合的交集
retainAll(Collection<?> c);
在当前集合中,仅保留参数集合和当前集合的交集
clear();
清空当前集合中所有数据内容
改
E set(int index, E e);
在 List 集合中,使用符合实例化对象过程中约束泛型对应具体数据类型对象,替换指定下标元素,返回值是被替换
元素对象本身
查
int size();
当前集合中有效元素个数
boolean isEmpty();
判断当前集合是否为空
boolean contains(Object obj);
判断参数对象是否在当前集合中存在
boolean containsAll(Collection<?> c);
判断参数集合是否是当前集合的子集合
Object[] toArray();
返回当前集合中所有元素对象的Object类型数组
E get(int index);
在当前集合中,获取指定下标元素
int indexOf(Object obj);
获取指定元素在当前集合中第一次出现的下标位置
int lastIndexOf(Object obj);
获取指定元素在当前集合中最后一次出现的下标位置
List<E> subList(int fromIndex, int toIndex);
从 fromIndex 下标开始,到 toIndex 下标结束,获取子集合对象,要求要头不要尾
list.remove(“道口烧鸡”):删其中一个,不会全部删除
package com.qf.list;
import java.util.ArrayList;
import java.util.List;
public class Demo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("重庆火锅");
list.add("成都火锅");
list.add("北京涮锅");
list.add("广东打边炉");
list.add(2,"南阳方城炝锅烩面");
System.out.println(list);// [重庆火锅, 成都火锅, 南阳方城炝锅烩面, 北京涮锅, 广东打边炉]
List<String> list2 = new ArrayList<>();
list2.add("安阳扁粉菜");
list2.add("道口烧鸡");
list2.add("红焖羊肉");
list2.add("许昌十八碗");
list2.add("虾子面");
list.addAll(2,list2);
System.out.println(list);// [重庆火锅, 成都火锅, 安阳扁粉菜, 道口烧鸡, 红焖羊肉, 许昌十八碗, 虾子面, 南阳方城炝锅烩面, 北京涮锅, 广东打边炉]
list.addAll(2, list2);// 允许重复
list.remove("道口烧鸡");
System.out.println("remove操作:" + list);
// remove操作:[重庆火锅, 成都火锅, 安阳扁粉菜, 红焖羊肉, 许昌十八碗, 虾子面, 安阳扁粉菜, 道口烧鸡, 红焖羊肉, 许昌十八碗, 虾子面, 南阳方城炝锅烩面, 北京涮锅, 广东打边炉]
String str = list.set(3,"洛阳牛肉汤");
System.out.println(str);// 红焖羊肉
System.out.println(list);// [重庆火锅, 成都火锅, 安阳扁粉菜, 洛阳牛肉汤, 许昌十八碗, 虾子面, 安阳扁粉菜, 道口烧鸡, 红焖羊肉, 许昌十八碗, 虾子面, 南阳方城炝锅烩面, 北京涮锅, 广东打边炉]
System.out.println(list.indexOf("道口烧鸡"));// 7
System.out.println(list.lastIndexOf("虾子面"));// 10
List<String> list3 = new ArrayList<>();
list3.add("虾子面");
list3.add("北京涮锅");
list3.add("洛阳牛肉汤");
System.out.println(list.containsAll(list3));// true
List<String> strings = list.subList(2, 5);
System.out.println(strings);// [安阳扁粉菜, 洛阳牛肉汤, 许昌十八碗]
}
}
1.5Set集合特征
Set 集合使用的方法都是 Collection 中包含的方法
【无序,不可重复】
Set 集合有两个实现类:
HashSet 底层是一个哈希表结构
TreeSet 底层是一个树形结构。
TreeSet 存储元素有数据要求
1. 存储数据有自然顺序。
2. 存储数据比较方式。
TreeSet存储元素问题解决
Comparable 接口,【增强接口】遵从当前接口的类可以认为是可比较的,可排序的,有比较方式的
public interface Comparable<T> { /** * Comparable 要求实现的方法,用于比较当前类型 * * @param t 增强类对应的数据类型 * @return 返回值为 0 表示两个元素大小关系一致,其他无所谓 */ int compareTo(T t); }
在需要排序,比较的结构中,会自动调用 compareTo 方法。
Person.java
public class Person implements Comparable<Person> {
private Integer id;
private String name;
private Integer age;
// 补充 Constructor Setter and Getter method
/*
当前 Person 类 遵从 Comparable 接口要求实现的方法,方法参数是泛型
通过【严格约束方式】在遵从接口的过程中,约束泛型对应的具体数据类型为
Person
*/
@Override
public int compareTo(Person o) {
System.out.println("Person 遵从 Comparable 接口实现 compareTo 方法,满足排序需求");
/*
this 表示调用当前方法的类对象, o 参数 Person 对象。
return this.age - o.age;
*/
return o.age - age;
}
}
TreeSet 存储 Comparable 增强的类对象
/**
* TreeSet 存储元素演示
*
* @author Anonymous 2023/2/28 14:42
*/
public class Demo4 {
public static void main(String[] args) {
TreeSet<Person> set = new TreeSet<>();
set.add(new Person(1, "张三", 16));
set.add(new Person(2, "张三", 36));
set.add(new Person(3, "张三", 26));
set.add(new Person(4, "张三", 6));
set.add(new Person(5, "张三", 66));
set.add(new Person(6, "张三", 56));
System.out.println(set);
}
}
增强 TreeSet,在实例化 TreeSet 构造方法中,提供 Comparator 接口的实现类对象,作为 TreeSet 增强。
Comparator 接口实现类对象,是给 TreeSet 提供处理目标类型的【比较器】
@FunctionalInterface public interface Comparator<T> { /** * Comparator 比较器方法,用于提供给需要排序的存储结构,执行排序算法提供比较操作规范和约束 * * @param o1 用户指定处理类型参数对象 * @param o2 用户指定处理类型参数对象 * @return 返回值为 0 表示两个元素大小关系一致,其他无所谓 */ int compare(T o1, T o2); }
Comparator 接口使用需要实现类来完成(JDK1.8 之前), JDK 1.8 之后可以用 Lambda表达式,方法引用
案例代码
package com.qfedu.b_collection;
import java.util.Comparator;
import java.util.TreeSet;
/*
针对于实现类遵从 Comparator 并且在遵从接口的过程中,【严格约束】
泛型对应的具体数据类型,当前数据类型为 Person ,针对于 Person 类型
的比较器,可以提供给 TreeSet 构造方法使用
*/
class MyComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
System.out.println("自定义比较器工作");
return o1.getAge() - o2.getAge();
}
}
/**
* @author Anonymous 2023/2/28 14:58
*/
public class Demo5 {
public static void main(String[] args) {
// 自定义比较器 Comparator 接口的实现类对象作为 TreeSet 构造方法参数
// 增强 TreeSet 结构,当前 TreeSet 可以存储 Person 类型
TreeSet<Person> set = new TreeSet<>(new MyComparator());
TreeSet<Person> set = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
System.out.println("匿名内部类自定义比较器工作");
return o1.getAge() - o2.getAge();
}
});
TreeSet<Person> set = new TreeSet<>((o1, o2) -> o1.getAge() - o2.getAge());
TreeSet<Person> set = new TreeSet<>(Comparator.comparing(Person::getAge));
TreeSet<Person> set = new TreeSet<>(Demo5::test);
set.add(new Person(1, "张三", 16));
set.add(new Person(2, "张三", 36));
set.add(new Person(3, "张三", 26));
set.add(new Person(4, "张三", 6));
set.add(new Person(5, "张三", 66));
set.add(new Person(6, "张三", 56));
set.forEach(System.out::println);
}
public static int test(Person o1, Person o2) {
System.out.println("方法引用");
return o1.getAge() - o2.getAge();
}
}
2.ArrayList性能分析和源码实现
2.1源码实现
底层是数组
增
add(E e);
添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
addAll(Collection<? extends E> c);
添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化过程中,约束泛
型对应具体数据类型,或者其子类类型。
add(int index, E e);
在指定下标位置,添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
addAll(int index, Collection<? extends E> c);
在指定下标位置,添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化
过程中,约束泛型对应具体数据类型,或者其子类类型。
删
E remove(int index);
在当前List集合中,删除指定下标元素,返回值是被删除元素对象本身
remove(Object obj);
在当前集合中,删除指定元素
removeAll(Collection<?> c);
在当前集合中,删除参数集合和当前集合的交集
retainAll(Collection<?> c);
在当前集合中,仅保留参数集合和当前集合的交集
clear();
清空当前集合中所有数据内容
改
E set(int index, E e);
在 List 集合中,使用符合实例化对象过程中约束泛型对应具体数据类型对象,替换指定下标元素,返回值是被替换
元素对象本身
查
int size();
当前集合中有效元素个数
boolean isEmpty();
判断当前集合是否为空
boolean contains(Object obj);
判断参数对象是否在当前集合中存在
boolean containsAll(Collection<?> c);
判断参数集合是否是当前集合的子集合
Object[] toArray();
返回当前集合中所有元素对象的Object类型数组
E get(int index);
在当前集合中,获取指定下标元素
int indexOf(Object obj);
获取指定元素在当前集合中第一次出现的下标位置
int lastIndexOf(Object obj);
获取指定元素在当前集合中最后一次出现的下标位置
List<E> subList(int fromIndex, int toIndex);
从 fromIndex 下标开始,到 toIndex 下标结束,获取子集合对象,要求要头不要尾
2.2实现过程问题
package com.qfedu.c_arrayList;
import java.util.Arrays;
/**
* @author Anonymous 2023/2/28 15:55
*/
public class MyArrayList<E> {
/**
* 底层用于存储元素的 Object 类型数组数据,可以支持任意类型
* 【目前】定义时直接初始化容量为 10
*/
private Object[] elementsData;
/**
* DEFAULT_CAPACITY 初始化底层数组默认容量,值为 10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* MyArrayList 底层数组允许的最大容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 记录当前集合中有效元素个数
*/
private int size = 0;
/**
* 无参数构造方法,实例化对象底层数组容量为 DEFAULT_CAPACITY
*/
public MyArrayList() {
elementsData = new Object[DEFAULT_CAPACITY];
}
/**
* 用户提供底层数组容量,用于实例化集合对象
*
* @param initCapacity 用户指定的数组容量
*/
public MyArrayList(int initCapacity) {
if (initCapacity < 0 || initCapacity > MAX_ARRAY_SIZE) {
throw new IllegalArgumentException("用户提供的容量参数不合法");
}
elementsData = new Object[initCapacity];
}
/**
* 添加一个元素当前集合中
*
* @param e 泛型约束限制的添加元素
* @return 添加成功返回 true
*/
public boolean add(E e) {
// return add(size, e);
if (size == elementsData.length) {
grow(size + 1);
}
elementsData[size++] = e;
return true;
}
/**
* 在指定下标位置添加另一个集合
*
* @param index 指定下标位置
* @param c 添加到当前集合的参数集合,要求集合中的元素内容和当前集合一致
* @return 添加成功返回 true
*/
public boolean addAll(int index, MyArrayList<? extends E> c) {
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException("数组下标越界异常");
}
// 1. 判断目前数组剩余容量是否满足添加操作需求
if (size + c.size() > elementsData.length) {
grow(size + c.size());
}
// 2. 从指定下标位置开始移动数据,移位
for (int i = size - 1; i >= index; i--) {
elementsData[i + c.size()] = elementsData[i];
/*
1 2 3 4 5 6 0 0 0 0 0
{1, 2, 3} ==> index = 2
1 2 1 2 3 3 4 5 6 0 0
arr[5 + c.size()] = arr[5];
arr[4 + c.size()] = arr[4];
arr[3 + c.size()] = arr[3];
arr[2 + c.size()] = arr[2];
*/
}
// 3. 放入数据到底层数组中
int count = 0;
for (int i = index; i < index + c.size(); i++) {
elementsData[i] = c.get(count++);
}
// 4. 有效元素个数 + 添加元素情况
size += c.size();
return true;
}
/**
* 添加参数集合到当前集合中,要求参数集合中存储的数据类型和当前集合数据类型一致
*
* @param c 参数集合,要求集合存储数据类型一致
* @return 添加成功返回 true
*/
public boolean addAll(MyArrayList<? extends E> c) {
return addAll(size, c);
}
/*
public boolean add(int index, E e) {
}
*/
/**
* 返回集合中有效元素个数
*
* @return 集合中有效元素个数
*/
public int size() {
return size;
}
/**
* 判断当前集合是否为空集合
*
* @return 如果为空集合,返回 true,如果不是,返回 null
*/
public boolean isEmpty() {
return 0 == size;
}
/**
* 清空整个集合
*/
public void clear() {
elementsData = new Object[size];
size = 0;
}
/**
* 获取当前集合中制定下标的元素
*
* @param index 指定下标
* @return 返回集合存储的元素对象
*/
@SuppressWarnings("unchecked")
public E get(int index) {
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException("下标越界");
}
/*
添加操作所有的方法元素都是 E 类型,从数组中提取元素是 Object 类型数据
为了方便使用,强转为 E 类型,类型转换没有任何的问题
*/
return (E) elementsData[index];
}
/**
* 返回当前集合的字符串信息描述
*
* @return 集合字符数据内容
*/
@Override
public String toString() {
return Arrays.toString(elementsData);
}
/**
* 从指定下标开始,到指定下标结束截取子集合,要头不要尾
*
* @param fromIndex 指定的开始下标
* @param toIndex 指定的结束下标
* @return 子集合对象
*/
public MyArrayList<E> subList(int fromIndex, int toIndex) {
if (fromIndex > toIndex || fromIndex < 0 || toIndex > size) {
throw new ArrayIndexOutOfBoundsException("数组下标越界");
}
// 根据当前限制的下标范围,实例化 MyArrayList 对象
MyArrayList<E> subList = new MyArrayList<>(toIndex - fromIndex);
for (int i = fromIndex; i < toIndex; i++) {
subList.add(get(i));
}
return subList;
}
/**
* 私有化底层扩容方法,在添加数据的操作过程中,控制底层数组容量
*
* @param minCapacity 最小容量要求
*ArrayList底层数组扩容方法流程概述:
*/
private void grow(int minCapacity) {
// 1. 获取当前底层数组容量
int oldCapacity = elementsData.length;
// 2. 计算得到新数组容量 大约 1.5 倍
int newCapacity = oldCapacity + oldCapacity / 2;
// 3. 判断新数组容量是否满足当前添加数据要求 ==> 原数组容量 + 当前添加的元素个数
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
// 4. 判断新数组容量是否超出 MAX_ARRAY_SIZE 【错误提示】
if (newCapacity > MAX_ARRAY_SIZE) {
throw new OutOfMemoryError();
}
// 5. 创建新数组
Object[] newArray = new Object[newCapacity];
// 6. 从原数组中移动数据到新数组,使用 size 更合适,是有效元素个数,避免无效拷贝
for (int i = 0; i < size; i++) {
newArray[i] = elementsData[i];
}
// 7. 新数组地址保存到底层数组引用数据类型变量中。
elementsData = newArray;
}
}
package com.qfedu.c_arrayList;
import org.junit.Test;
/**
* @author Anonymous 2023/2/28 17:56
*/
public class MyArrayListTest {
@Test
public void test1() {
MyArrayList<String> list = new MyArrayList<>();
list.add("热干面");
list.add("扁粉菜");
list.add("酸辣粉");
list.add("牛肉面");
System.out.println(list);
MyArrayList<String> list1 = new MyArrayList<>();
list1.add("担担面");
list1.add("biangbiang面");
list1.add("油泼面");
list1.add("老碗面");
list1.add("刀削面");
list1.add("湖南牛肉面");
list1.add("老北京方便面");
list1.add("老北京炸酱面");
list.addAll(1, list1);
System.out.println(list);
MyArrayList<String> subList = list.subList(2, 8);
System.out.println(subList);
}
}
2.3ArrayList性能分析
增
1.数据添加有可能导致扩容,扩容效率低。
2.在数组中添加元素,会导致数据整体向后移,效率低
删
1.在数组中删除元素,会导致数据整体向前移,效率低
2.删除操作会导致有效元素个数和当前底层数组容量占比降低,浪费内存空间。
数组缩容方法:trimToSize();
改/查
E set(int index , E e)
数组下标操作效率极高,修改,查询极高
list.add(“牛肉面”);
System.out.println(list);
MyArrayList<String> list1 = new MyArrayList<>();
list1.add("担担面");
list1.add("biangbiang面");
list1.add("油泼面");
list1.add("老碗面");
list1.add("刀削面");
list1.add("湖南牛肉面");
list1.add("老北京方便面");
list1.add("老北京炸酱面");
list.addAll(1, list1);
System.out.println(list);
MyArrayList<String> subList = list.subList(2, 8);
System.out.println(subList);
}
}
## 2.3ArrayList性能分析
增
1.数据添加有可能导致扩容,扩容效率低。
2.在数组中添加元素,会导致数据整体向后移,效率低
删
1.在数组中删除元素,会导致数据整体向前移,效率低
2.删除操作会导致有效元素个数和当前底层数组容量占比降低,浪费内存空间。
数组缩容方法:trimToSize();
改/查
E set(int index , E e)
数组下标操作效率极高,修改,查询极高