Java笔记_12
- 一、集合的体系结构
- 二、Collection
- 2.1、迭代器遍历
- 2.2、增强for遍历
- 2.3、Lambda表达式遍历
- 三、list集合
- 3.1、List集合的特有方法
- 3.2、 List集合的遍历方式
- 四、数据结构
- 4.1、数据结构概述
- 4.2、栈
- 4.3、队列
- 4.4、数组
- 4.5、链表
- 4.6、树
- 二叉查找树
- 平衡二叉树
- 4.7、红黑树
- 五、底层原理
- 5.1、ArrayList集合底层原理
- 5.2、LinkedList底层源码
- 5.3、迭代器的底层源码
- 六、泛型
- 6.1、泛型深入
- 6.2、泛型方法
- 6.3、泛型接口
- 6.4、泛型的继承和通配符
- 七、Set集合
- 7.1、HashSet
- 7.2、HashSet案例
- 7.3、LinkedHashSet
- 7.4、TreeSet
一、集合的体系结构
二、Collection
List系列集合:添加的元素时有序、可重复、有索引
Set系列集合:添加的元素是无序、不重复、无索引
Collection
- Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
方法名称 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数/集合的长度 |
注意点:
Collection是一个接口,我们不能直接创建他的对象。所以,现在我们学习他的方法时,只能创建他实现类的对象。
实现类: ArrayList
-
add
添加元素- 细节1:如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的。
- 细节2:如果我们要往Set系列集合中添加数据,如果当前要添加元素不存在,方法返回true,表示添加成功。
如果当前要添加的元素已经存在,方法返回false,表示添加失败。(Set系列集合不允许重复)
-
remove
删除- 细节1:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行别除。
- 细节2:方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false
- 如果要删除的元素不存在,就会删除失败。
-
contains
判断包含- 细节:底层是依赖equals方法进行判断是否存在的。
- 所以,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法。
2.1、迭代器遍历
迭代器遍历
迭代器在Java中的类是lterator
,迭代器是集合专用的遍历方式。
Collection集合获取迭代器
方法名称 | 说明 |
---|---|
Iterator<E> iterator() | 返回迭代器对象,默认指向当前集合的0索引 |
lterator中的常用方法
方法名称 | 说明 |
---|---|
boolean hasNext() | 判断当前位置是否有元素,有元素返回true ,没有元素返回false |
E next() | 获取当前位置的元素,并将迭代器对象移向下一个位置。 |
package CollectionDome;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class lteratorDome1 {
public static void main(String[] args) {
//1.创建集合对象
Collection<String> coll = new ArrayList<>();
//2.添加集合成员
coll.add("asd");
coll.add("qwe");
coll.add("aaa");
//3.创建迭代器对象
Iterator it = coll.iterator();
//利用循环获取里面的数据
while (it.hasNext()){
//next的两个作用:
// 1.获取元素
// 2.指针到下一个元素
Object next = it.next();
System.out.println(next);
}
}
}
细节注意点:
- 报错NoSuchElementException
- 迭代器遍历完毕,指针不会复位
- 循环中只能用一次next方法
- 迭代器遍历时,不能用集合的方法进行增加或者删除,使用
迭代器名.remove()
的方法移除
2.2、增强for遍历
- 增强for的底层就是迭代器,为了简化迭代器的代码书写的
- 它是JDK5之后出现的,其内部原理就是一个lterator迭代器
- 所有的单列集合和数组才能用增强for进行遍历。
格式
for(元素的数据类型 变量名 : 数组或者集合){
}
//例:
for(String s : list){
System.out.println(s);
}
package CollectionDome;
import java.util.ArrayList;
import java.util.Collection;
public class Dome3 {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("asd");
coll.add("aaa");
coll.add("qweqe");
coll.add("ssss");
coll.add("dddd");
//s为一个第三方变量,不改变集合的值
for (String s:coll){
System.out.println(s);
}
}
}
2.3、Lambda表达式遍历
得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。
方法名 | 说明 |
---|---|
default void forEach(Consumer<? super T> action): | 结合lambda遍历集合 |
package CollectionDome;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class Dome3 {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("asd");
coll.add("aaa");
coll.add("qweqe");
coll.add("ssss");
coll.add("dddd");
//s为一个第三方变量
//匿名内部类
coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//lambda表达式
coll.forEach(s->System.out.println(s));
}
}
- 三种通用的遍历方式:
- 迭代器:在遍历的过程中需要删除元素,请使用迭代器。
- 增强for、Lambda:
仅仅想遍历,那么使用增强for或Lambda表达式。
三、list集合
List集合特点
- 有序:存和取的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
3.1、List集合的特有方法
- Collection的方法List都继承了
- List集合因为有索引,所以多了很多索引操作的方法
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
add
添加- 细节: 原来索引上的元素会依次往后移
remove
删除- 细节:当集合元素和下标相同时,会优先删除下标表示的成员。因为在调用方法的时候,如果方法出现了重载现象优先调用,实参跟形参类型一致的那个方法。
package CollectionDome;
import java.util.ArrayList;
import java.util.List;
public class Dome4 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//添加元素
list.add("aaa");
list.add("bbb");
list.add("ddd");
list.add("sss");
//list系列中的add方法
list.add(1,"qqq");
System.out.println(list);
//删除元素(使用元素值)
list.remove("aaa");
System.out.println(list);
//删除元素(使用下标)
list.remove(1);
System.out.println(list);
//创建一个新的元素
list.set(1,"asd");
System.out.println(list);
//得到该索引的元素
System.out.println(list.get(1));
}
}
3.2、 List集合的遍历方式
五种迭代方式:
- 迭代器遍历
- 列表迭代器遍历
- 增强for遍历
- Lambda表达式遍历
- 不同for循环(因为List集合存在索引)
- 列表迭代器遍历
package CollectionDome;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Dome5 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//添加元素
list.add("aaa");
list.add("bbb");
list.add("ddd");
list.add("sss");
//列表迭代器
//获取一个列表迭代器的对象,里面的指针默认也是指向0索引的
//额外添加了一个方法:在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while (it.hasNext()){
String str = it.next();
if ("bbb".equals(str)){
it.add("qqq");
}
}
System.out.println(list);
}
}
四、数据结构
4.1、数据结构概述
数据结构是计算机底层存储、组织数据的方式。
是指数据相互之间是以什么方式排列在一起的。
数据结构是为了更加方便的管理和使用数据,需要结合具体的业务场景来进行选择。
一般情况下,精心选择的数据结构可以带来更高的运行或者存储效率。
4.2、栈
栈的特点:后进先出,先进后出
数据进入栈模型的过程称为:压/进栈
数据离开栈模型的过程称为:弹/出栈
4.3、队列
队列的特点:先进先出,后进后出
数据从后端进入队列模型的过程称为:入队列
数据从前端离开队列模型的过程称为:出队列
4.4、数组
数组是一种查询快,增删慢的模型
- 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
- 删除效率低:要将原始数据删除,同时后面每个数据前移。
- 添加效率极低:添加位置后的每个数据后移,再添加元素。
4.5、链表
- 链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
- 链表查询慢,无论查询哪个数据都要从头开始找
- 链表增删相对快
各种数据结构的特点和作用是什么样的
- 栈:后进先出,先进后出。
- 队列:先进先出,后进后出。
- 数组:内存连续区域,查询快,增删慢。
- 链表:元素是游离的,查询慢,首尾操作极快。
4.6、树
- 度:每个节点的子节点数量
- 二叉树中,任意节点的度<=2
- 树高:树的总层数
- 根节点:最顶层的节点
- 左子节点:左下方的节点
- 右子节点:右下方的节点
- 根节点的左子树:蓝色虚线
- 根节点的右子树:绿色虚线
一个节点上的结构
二叉查找树
二叉查找树,又称二叉排序树或者二叉搜索树
特点:
- 每一个节点上最多有两个子节点
- 任意节点左子树上的值都小于当前节点
- 任意节点右子树上的值都大于当前节点
添加节点
- 小的存左边
- 大的存右边
- 一样的不存
遍历方式
- 前序遍历:当前节点,左子节点,右子结点
- 中序遍历:左子节点,当前节点,右子结点
- 后序遍历:左子节点,右子结点,当前节点
- 层序遍历:一层一层的去遍历
平衡二叉树
规则:任意节点左右子树高度差不超过1
平衡二叉树的旋转机制
规则1:左旋
规则2:右旋
触发时机:当添加一个节点之后,该树不再是一颗平衡二叉树
-
左(右)旋步骤:
- 确定支点:从添加的节点开始,不断的往父节点找不平衡的节点
- 以不平衡点作为支点
- 将跟节点的右侧往左(右)拉
- 原先的右(左)子节点变成新的父节点,并把多余的左(右)子节点出让,给已经降级的根节点当右(左)子节点
-
数据结构(平衡二叉树)需要旋转的四种情况
- 左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡
- 一次右旋
- 左右:当根节点左子树的右子树有节点插入,导致二叉树不平衡
- 先局部左旋,再整体右旋(先变成左左型,再变成平衡二叉树)
- 右右:当根节点右子树的右子树有节点插入,导致二叉树不平衡
- 一次左旋
- 右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡
- 先局部右旋,再整体左旋(先变成右右型,再变成平衡二叉树)
- 左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡
4.7、红黑树
- 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
- 1972年出现,当时被称之为平衡二叉B树。后来,1978年被修改为如今的"红黑树"。
- 它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,
- 每一个节点可以是红或者黑,红黑树不是高度平衡的,它的平衡是通过"红黑规则"进行实现的
- 平衡二叉树
- 高度平衡
- 当左右子树高度差超过1时,通过旋转保持平衡
- 红黑树
- 是一个二叉查找树
- 但是不是高度平衡的
- 条件 :特有的红黑规则
数据结构(红黑树)红黑规则:
- 每一个节点或是红色的,或者是黑色的
- 根节点必须是黑色
- 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
- 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
- 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
添加节点的规则
- 红黑树在添加节点的时候,添加的节点默认是红色的
五、底层原理
5.1、ArrayList集合底层原理
- 利用空参创建的集合,在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建一个新的长度为10的数组
- 存满时,会扩容1.5倍
- 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
5.2、LinkedList底层源码
- 底层数据结构是双链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的。
- LinkedList本身多了很多直接操作首尾元素的特有API。
特有方法 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLlst(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
添加第一个链表元素时:
添加多个链表元素时:
5.3、迭代器的底层源码
六、泛型
6.1、泛型深入
- 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
- 泛型的格式:<数据类型>
- 注意:泛型只能支持引用数据类型。
如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
此时可以往集合添加任意的数据类型。
带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。
泛型的好处
- 统一数据类型。
- 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。
扩展知识点:Java中的泛型是伪泛型
泛型的细节:
- 泛型中不能写基本数据类型
- 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
- 如果不写泛型,类型默认事Object
泛型可以在很多地方进行定义
使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型,可以写成:T、E、K、V等
package CollectionDome.Generics_Dome;
import java.util.Arrays;
//编写一个类的时候,如果不确定类型,那么这个类就可以定义为E
public class MyArrayList<E> {
int size;
Object[] obj = new Object[10];
/*
* E:表示不确定的类型,该类型在类名后面已经定义过了
* e:形参的名字,变量名
* */
public boolean add(E e){
obj[size] = e;
size++;
return true;
}
public E get(int index){return (E)obj[index];}
@Override
public String toString(){
return Arrays.toString(obj);
}
}
package CollectionDome.Generics_Dome;
import java.sql.SQLOutput;
public class Test {
public static void main(String[] args) {
MyArrayList<String> m1 = new MyArrayList<>();
m1.add("aaa");
m1.add("sss");
m1.add("ddd");
System.out.println(m1.get(1));
}
}
6.2、泛型方法
方法中形参类型不确定时
- 使用类名后面定义的泛型——所有方法都能使用
- 在方法申明上定义自己的泛型——只有本方法能用
此处T可以理解为变量,但是不是用来记录数据的,而是记录类型的,可以写成:T、E、K、V等
package CollectionDome.Generics_Dome2;
import java.util.ArrayList;
import java.util.List;
public class ListUtil {
private ListUtil(){};
public static<E> void addAll(ArrayList<E> list,E e1, E e2,E e3,E e4,E e5){
list.add(e1);
list.add(e2);
list.add(e3);
list.add(e4);
}
public static<E> void addAll2(ArrayList<E> list,E ...e){
for (E element:e){
list.add(element);
}
}
}
package CollectionDome.Generics_Dome2;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> a1 = new ArrayList<>();
ListUtil.addAll(a1,"aaa","sss","cccc","ass","ccsc");
System.out.println(a1);
ArrayList<Integer> a2 = new ArrayList<>();
ListUtil.addAll(a2,111,222,12321,232,545);
System.out.println(a2);
ArrayList<Integer> a3 = new ArrayList<>();
ListUtil.addAll2(a3,111,222,12321,232,545,1121,3232,12321321);
System.out.println(a3);
}
}
6.3、泛型接口
重点:如何使用一个带泛型的接口
方式1:实现类给出具体类型
方式2:实现类延续泛型,创建对象时再确定
方式一:
package CollectionDome.Generics_Dome3;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class MyArraysList1 implements List<String> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(Object o) {
return false;
}
@Override
public Iterator<String> iterator() {
return null;
}
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a) {
return null;
}
@Override
public boolean add(String s) {
return false;
}
@Override
public boolean remove(Object o) {
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return false;
}
@Override
public boolean addAll(Collection<? extends String> c) {
return false;
}
@Override
public boolean addAll(int index, Collection<? extends String> c) {
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
return false;
}
@Override
public boolean retainAll(Collection<?> c) {
return false;
}
@Override
public void clear() {
}
@Override
public String get(int index) {
return null;
}
@Override
public String set(int index, String element) {
return null;
}
@Override
public void add(int index, String element) {
}
@Override
public String remove(int index) {
return null;
}
@Override
public int indexOf(Object o) {
return 0;
}
@Override
public int lastIndexOf(Object o) {
return 0;
}
@Override
public ListIterator<String> listIterator() {
return null;
}
@Override
public ListIterator<String> listIterator(int index) {
return null;
}
@Override
public List<String> subList(int fromIndex, int toIndex) {
return null;
}
}
方式二:
package CollectionDome.Generics_Dome3;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class MyArrayList<E> implements List<E> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(Object o) {
return false;
}
@Override
public Iterator<E> iterator() {
return null;
}
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a) {
return null;
}
@Override
public boolean add(E e) {
return false;
}
@Override
public boolean remove(Object o) {
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return false;
}
@Override
public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
return false;
}
@Override
public boolean retainAll(Collection<?> c) {
return false;
}
@Override
public void clear() {
}
@Override
public E get(int index) {
return null;
}
@Override
public E set(int index, E element) {
return null;
}
@Override
public void add(int index, E element) {
}
@Override
public E remove(int index) {
return null;
}
@Override
public int indexOf(Object o) {
return 0;
}
@Override
public int lastIndexOf(Object o) {
return 0;
}
@Override
public ListIterator<E> listIterator() {
return null;
}
@Override
public ListIterator<E> listIterator(int index) {
return null;
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
return null;
}
}
6.4、泛型的继承和通配符
泛型的通配符:?
?
也表示不确定的类型他可以进行类型的限定? extends E
:表示可以传递E或者E所有的子类类型? super E
:表示可以传递E或者E所有的父类类型
package CollectionDome.Generics_Dome4;
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
ArrayList<ye> l1 = new ArrayList<>();
ArrayList<fu> l2 = new ArrayList<>();
ArrayList<zi> l3 = new ArrayList<>();
ArrayList<student> l4 = new ArrayList<>();
method(l1);
method(l2);
method(l3);
//由于student不是继承于ye类所以不能使用该方法
//method(l4);
}
/*
* 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据。
* 弊端:
利用泛型方法有一个小弊端,此时他可以接受任意的数据类型
* Ye Fu zi student
希望:本方法虽然不确定类型,但是以后我希望只能传递Ye Fu Zi
此时我们就可以使用泛型的通配符:?
?也表示不确定的类型
他可以进行类型的限定
? extends E:表示可以传递E或者E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型
应用场景:
1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符
泛型的通配符:
关键点:可以限定类型的范围
* */
public static void method(ArrayList<? extends ye> list){
}
static class ye{ }
static class fu extends ye{}
static class zi extends fu{}
static class student{}
}
package CollectionDome.Generics_Dome5;
import java.beans.beancontext.BeanContextServiceProviderBeanInfo;
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
ArrayList<Dog_taidi> taidis = new ArrayList<>();
ArrayList<Dog_hashiqi> hashiqis = new ArrayList<>();
ArrayList<Cat_bosi> bosi = new ArrayList<>();
ArrayList<Cat_lihua> lihua = new ArrayList<>();
Dog_taidi t1 = new Dog_taidi("泰泰",1);
Dog_taidi t2 = new Dog_taidi("迪迪",2);
Dog_hashiqi h1 = new Dog_hashiqi("哈哈",1);
Dog_hashiqi h2 = new Dog_hashiqi("时时",2);
Cat_bosi b1 = new Cat_bosi("波波",1);
Cat_bosi b2 = new Cat_bosi("思思",2);
Cat_lihua l1 = new Cat_lihua("梨梨",1);
Cat_lihua l2 = new Cat_lihua("花花",2);
taidis.add(t1);
taidis.add(t2);
hashiqis.add(h1);
hashiqis.add(h2);
bosi.add(b1);
bosi.add(b2);
lihua.add(l1);
lihua.add(l2);
KeepPet.keepPetDog(taidis);
KeepPet.keepPetDog(hashiqis);
KeepPet.keepPetAnimal(taidis);
KeepPet.keepPetCat(bosi);
KeepPet.keepPetCat(lihua);
}
}
package CollectionDome.Generics_Dome5;
import java.util.ArrayList;
public class KeepPet {
private KeepPet(){};
public static void keepPetAnimal(ArrayList<? extends Animal> list){
for (Animal a:list){
a.eat();
}
}
public static void keepPetDog(ArrayList<? extends Dog> list){
for (Dog d :list){
d.eat();
}
}
public static void keepPetCat(ArrayList<? extends Cat> list){
for (Cat c:list){
c.eat();
}
}
}
package CollectionDome.Generics_Dome5;
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
abstract void eat();
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Animal{name = " + name + ", age = " + age + "}";
}
}
package CollectionDome.Generics_Dome5;
public abstract class Cat extends Animal {
public Cat(String name,int age ){
super(name, age);
}
}
package CollectionDome.Generics_Dome5;
public class Cat_bosi extends Cat{
public Cat_bosi(String name, int age) {
super(name, age);
}
@Override
void eat() {
System.out.println("一只叫"+Cat_bosi.super.getName() +"的,"+ Cat_bosi.super.getAge()+"岁的波斯猫,正在吃小鱼干");
}
}
package CollectionDome.Generics_Dome5;
public class Cat_lihua extends Cat{
public Cat_lihua(String name, int age) {
super(name, age);
}
@Override
void eat() {
System.out.println("一只叫"+Cat_lihua.super.getName() +"的,"+ Cat_lihua.super.getAge()+"岁的狸花猫,正在吃小鱼干");
}
}
package CollectionDome.Generics_Dome5;
public abstract class Dog extends Animal{
public Dog(String name,int age ){
super(name, age);
}
}
package CollectionDome.Generics_Dome5;
public class Dog_hashiqi extends Dog{
public Dog_hashiqi(String name, int age) {
super(name, age);
}
@Override
void eat() {
System.out.println("一只叫做"+Dog_hashiqi.super.getName() +"的,"+Dog_hashiqi.super.getAge() +"岁的哈士奇,正在吃骨头,边吃边蹭");
}
}
package CollectionDome.Generics_Dome5;
public class Dog_taidi extends Dog{
public Dog_taidi(String name, int age) {
super(name, age);
}
@Override
void eat() {
System.out.println("一只叫做"+Dog_taidi.super.getName() +"的,"+Dog_taidi.super.getAge() +"岁的泰迪,正在吃骨头,边吃边蹭");
}
}
哪里定义泛型?
- 泛型类:在类名后面定义泛型,创建该类对象的时候,确定类型
- 泛型方法:在修饰符后面定义方法,调用该方法的时候,确定类型
- 泛型接口:在接口名后面定义泛型,实现类确定类型,实现类延续泛型
- 泛型不具备继承性,但是数据具备继承性
七、Set集合
Set系列集合
- 无序:存取顺序不一致
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
Set集合的实现类
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- Treeset:可排序、不重复、无索引
Set接口中的方法上基本上与Collection的API一致。
7.1、HashSet
HashSet底层原理
- HashSet集合底层采取哈希表存储数据
- 哈希表是一种对于增删改查数据性能都较好的结构
哈希表组成
- JDK8之前:数组+链表
- JDK8开始:数组+链表+红黑树
哈希值
- 根据hashcode方法算出来的int类型的整数
- 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
- 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象的哈希值特点
- 如果没有重写hashcode方法,不同对象计算出的哈希值是不同的
- 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
- 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
HashSet底层原理
- 创建一个默认长度16,默认加载因子0.75的数组,数组名table
- 根据元素的哈希值跟数组的长度计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入
- 如果位置不为null,表示有元素,则调用equals方法比较属性值
- 一样:不存
- 不一样:存入数组,形成链表
- JDK8以前:新元素存入数组,老元素挂在新元素下面
- JDK8以后:新元素直接挂在老元素下面
- JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树
- 如果集合中存储的是自定义对象,必须要重写hashCode和equals方法
小结:
- Hashset集合的底层数据结构是什么样的?
链表和红黑树 - HashSet添加元素的过程?
根据元素的哈希值跟数组的长度计算出应存入的位置,然后存入,如果有数据了就判断两者是否相同,相同则去除,不相同则挂到当前链表下方 - Hashset为什么存和取的顺序不一样?
存储的位置是随机的,不确定从哪个位置开始取出来 - HashSet为什么没有索引?
一个索引下可能有多个数据,使用索引查找没办法取到准确的数据 - Hashset是利用什么机制保证去重的?
利用HashCode方法和equals方法
7.2、HashSet案例
package CollectionDome.SetDome;
import java.util.HashSet;
import java.util.Set;
public class Dome2 {
public static void main(String[] args) {
Set<Student> s = new HashSet<>();
Student s1 = new Student("zhangsan",18);
Student s2 = new Student("xiaowang",18);
Student s3 = new Student("laowang",19);
Student s4 = new Student("xiaowang",18);
s.add(s1);
s.add(s2);
s.add(s3);
s.add(s4);
System.out.println(s);
}
}
package CollectionDome.SetDome;
import javax.sql.rowset.spi.SyncResolver;
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", 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);
}
}
7.3、LinkedHashSet
- 底层原理
- 有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是依然哈希费,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
7.4、TreeSet
特点:
- 不重复、无索引、可排序
- 可排序:按照元素的默认规则(有小到大)排序。
- TreeSet集合底层是基升红黑树的数据结构实现排序的,增删改查性能都较好。
package CollectionDome.SetDome;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.function.Consumer;
public class TreeSetDome1 {
public static void main(String[] args) {
TreeSet<Integer> t = new TreeSet<>();
t.add(1);
t.add(9);
t.add(8);
t.add(6);
t.add(8);
t.add(4);
t.add(2);
t.add(7);
System.out.println(t);
System.out.println("------------------");
Iterator it = t.iterator();
while (it.hasNext()){
Object next = it.next();
System.out.println(next);
}
for (Integer i:t){
System.out.println(i);
}
t.forEach(integer->System.out.println(integer));
}
}
TreeSet集合默认的规则
- 对于数值类型: Integer , Double,默认按照从小到大的顺序进行排序。
- 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序。
TreeSet的两种比较方式:
- 方式一:
默认排序/自然排序:Javabean类实现Comparable接口指定比较规则
package CollectionDome.SetDome;
import java.util.TreeSet;
public class TreeSetDome2 {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>();
Student s4 = new Student("xiaoli",23);
Student s5 = new Student("xiaowang",24);
Student s6 = new Student("xiaozhang",25);
Student s1 = new Student("xiaoming",20);
Student s2 = new Student("xiaohong",22);
Student s3 = new Student("xiaolu",21);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s1);
ts.add(s2);
ts.add(s3);
System.out.println(ts);
}
}
package CollectionDome.SetDome;
import javax.sql.rowset.spi.SyncResolver;
import java.util.Comparator;
import java.util.Objects;
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public int compareTo(Student o) {
System.out.println("this:"+this);
System.out.println("o:"+o);
return this.getAge()-o.getAge();
}
}
- 方式二:
比较器排序:创建TreeSet对象时候,传递比较器Comparator指定规则
使用原则:默认使用第一种,如果第一种不能满足当前需求,就使用第二种
package CollectionDome.SetDome;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDome3 {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<>((String o1, String o2)->{
//比较长度来排序
int i = o1.length()-o2.length();
i = i==0?o1.compareTo(o2):i;
return i;
}
);
ts.add("asd");
ts.add("qwe");
ts.add("zzza");
ts.add("aaaz");
ts.add("sdsasdasd");
ts.add("jkl");
ts.add("tyh");
System.out.println(ts);
}
}
package CollectionDome.SetDome;
import java.util.TreeSet;
public class stu1_Dome4 {
public static void main(String[] args) {
TreeSet<student1> ts = new TreeSet<>();
student1 s1 = new student1("xiaoming",18,89,99,78);
student1 s2 = new student1("xiaowang",19,87,99,80);
student1 s3 = new student1("xiaoli",18,86,100,80);
student1 s4 = new student1("xiaozhang",17,95,90,80);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
System.out.println(ts);
}
}
package CollectionDome.SetDome;
public class student1 implements Comparable<student1>{
private String name;
private int age;
private int Chinese;
private int Math;
private int English;
public student1() {
}
public student1(String name, int age, int Chinese, int Math, int English) {
this.name = name;
this.age = age;
this.Chinese = Chinese;
this.Math = Math;
this.English = English;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return Chinese
*/
public int getChinese() {
return Chinese;
}
/**
* 设置
* @param Chinese
*/
public void setChinese(int Chinese) {
this.Chinese = Chinese;
}
/**
* 获取
* @return Math
*/
public int getMath() {
return Math;
}
/**
* 设置
* @param Math
*/
public void setMath(int Math) {
this.Math = Math;
}
/**
* 获取
* @return English
*/
public int getEnglish() {
return English;
}
/**
* 设置
* @param English
*/
public void setEnglish(int English) {
this.English = English;
}
public String toString() {
return "student1{name = " + name + ", age = " + age + ", Chinese = " + Chinese + ", Math = " + Math + ", English = " + English + "}";
}
@Override
public int compareTo(student1 o) {
int sum1 = this.getChinese()+this.getEnglish()+this.getMath();
int sum2 = o.getChinese()+o.getEnglish()+o.getMath();
int i= sum1-sum2;
i = i==0?this.getChinese()-o.getChinese():i;
i = i==0?this.getMath()-o.getMath():i;
i = i==0?this.getEnglish()-o.getEnglish():i;
i = i==0?this.getName().compareTo(o.getName()):i;
return i;
}
}