JavaSE进阶——Day05 泛型、数据结构、List集合、Set集合

JavaSE进阶——Day05 该篇主要讲解Java中的泛型、数据结构、List集合和Set集合

泛型

什么是泛型?

是一种或类型参数,可以来设置存储数据类型

泛型解决程序中的什么问题?

  • 限制集和中的数据类型

  • 泛型是使用在代码编写时的一种技术方式(编译期技术)

  • 泛型使用后不用再进行数据类型的强转
  • 泛型在程序运行后就会擦除

泛型如何使用?

泛型类

当不确定属类中某个属性类型时,可以使用泛型表示

1
2
3
4
5
6
7
8
9
10
11
public class 类名<E>{ /* E就是泛型名 */
}

// 当不确定属类中某个属性类型时,可以使用泛型表示
public class 泛型类<T>{
private T 变量;
}

// 在创建泛型类对象时,明确类型
泛型类<Integer> 对象引用 = new 泛型类<Integer>(); //泛型类中成员变量的类型为:Integer
泛型类<String> 对象引用 = new 泛型类<String>(); //泛型类中成员变量的类型为:String

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package io.github.ethanliu6.genericity;

/**
* @Author EthanLiu 16693226842@163.com
* @Date 2024/4/19 18:32
* @Project JavaCode_SE_Advance
* @Theme 泛型
*/
public class Demo01 {
public static void main(String[] args) {
GenericityClass<String> gc1 = new GenericityClass<>(); //5版本后可以在后面不添加类型
gc1.setObj("Ethan Love Qiu");
System.out.println(gc1.getObj());

GenericityClass<Integer> gc2 = new GenericityClass<>();
gc2.setObj(8888);
System.out.println(gc2.getObj());

}
}

// 当不确定属类中某个属性类型时,可以使用泛型表示
class GenericityClass<T> {
private T obj;

public T getObj() {
return obj;
}

public void setObj(T obj) {
this.obj = obj;
}
}

泛型接口

当不确定接口中某个方法参数使用什么类型或者方法返回值类型时,可以使用泛型表示

1
2
3
4
5
6
7
public interface 泛型接口<E>{ /* E就是泛型名 */
public void method(E e){

}
}

// 当不确定接口中某个方法参数使用什么类型或者方法返回值类型时,可以使用泛型表示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 情况1:在子类编写时,指定接口上泛型的具体类型
public class 子类 implements 泛型接口<String> {
//方法重写
@Override
public void method(String e) /* T --> String */ {
}
}

// 情况2:在子类编写时,没有指定接口上泛型的具体类型,意味着:子类也使用了跟泛型接口同样的泛型(子类 --> 泛型类)
public class 子类<E> implements 泛型接口<E> {
//方法重写
public void method(E e){

}
}

注意:在创建子类对象时,指定类型
子类<Integer> 子类引用名 = new 子类<>();

泛型方法

当前类没有声明为泛型类,当方法不知道参数类型或者返回值类型时,使用泛型方法

1
2
3
4
//语法格式
修饰符号 <泛型> 返回值类型 方法名(<参数泛型> 参数1, ……){
//方法体
}
1
2
3
4
public  <T> method(<E> param){
//……
}
//当调用方法时, 传入的参数类型就是泛型类型

泛型梳理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
基础泛型的定义:
1、泛型类
2、泛型接口
3、泛型方法

问题:开发时选择哪个泛型定义?
1,先明确要定义的是接口还是类
当类中的成员变量,在不确定具体使用哪种类型时,可以使用:泛型 表示类型
当接口中方法,在不确定方法参数类型或方法返回值类型时,可以使用:泛型 表示类型
2,在编写工具类时,工具类中的方法参数或方法返回值,不确定具体类型, 可以使用: 泛型


问题:泛型定义的语法
//泛型类
public class 类名<T,E,X,Y>{
private T name;
private E a;
private X n;
private Y m;

public 类名(T name , E a , X n ,Y m){
}

}
new 类名<String,Integer,Double,Student>();


//泛型接口
public interface 接口名<T,E>{
}

//泛型方法
public <T> T 方法名(T 参数名){
}


问题: 怎么明确泛型指定的具体类型(指定泛型上的类型)
//泛型类: 在创建对象时,指定泛型类型
类名<String> 对象 = new 类名<>();


//泛型接口: 1.在子类实现接口时,明确指定了泛型类型 2.子类继承接口上的泛型(子类变为泛型类)
interface 接口<T>{
public void method(T n);
public T get();
}
class 子类 implements 接口<Integer>{
public void method(Integer n){
}
public Integer get(){
}
}

class 子类<T> implements 接口<T>{
public void method(T n){
}
public T get(){
}
}

//泛型方法:
public class 类{

public <T> T getInstance(T param){
}

}
Student stu = 类对象.getInstance( new Student() )

泛型通配符

  • 泛型通配符:<?>(任意类型)
  • 通常在开发中,?与泛型的上下限一起使用

受限泛型

泛型下限:

1
2
3
4
5
//指定的泛型中的最小类型
<? super 最小类型>

?可以是该最小类型
?也可以是该类型的父类型

泛型上限:

1
2
3
4
5
//指定泛型中的最大类型
<? extends 最大类型>

?可以是该最大类型
?也可以是该最大类型的子类型

例如:

image-20240420173727772

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package io.github.ethanliu6.genericity.demo03;

import java.util.ArrayList;

/**
* @Author EthanLiu 16693226842@163.com
* @Date 2024/4/20 17:21
* @Project JavaCode_SE_Advance
* @Theme 泛型限制
*/
public class Test2 {
public static void main(String[] args) {
ArrayList<Person> people = new ArrayList<>();
people.add(new Student(126, "202234004026"));
people.add(new Worker());
show1(people);
show2(people);

System.out.println("\n+++++++++++++++++\n");
ArrayList<Worker> workers = new ArrayList<>();
workers.add(new Worker());
// workers.add(new Student(123, "23453456")); 泛型限制
show1(workers);
// show2(workers); 编译出错,泛型下限受限
}

public static void show1(ArrayList<? extends Person> people) {
System.out.println("show1是泛型上限");
}

public static void show2(ArrayList<? super Student> E){
System.out.println("show2是泛型下限");
}
}

数据结构

这里只是简单的学习数据结构,并非408中的系统性数据结构

数据结构的认识

数据结构:数据存储时的一种排列方式

类型:

  • 栈(先进后出)

  • 队列(先进先出)//数组就是这种方式

    数组结构:

    1. 数组在内存中的体现是一块连续存储数据的空间

    2. 特点是查询快、增删元素慢

    3. ArrayList集合底层就是使用:数组结构
  • 链表

  • 哈希表

    • 二叉树
    • 平衡二叉树
    • 红黑树
    • ……

队列

平衡二叉树

红黑树

List集合

Java语言中集合体系划分:

  • Collection (接口)
  • Map (接口)

java.util.Collection集合: 是一个接口(无法实例化)

  • java.util.List集合(接口)
    • 常用子类:ArrayList 、 LinkedList
  • java.util.Set集合(接口)

image-20240420193210344

java.util.List集合:

  • 带有索引
  • 存储的元素的顺序和获取元素的顺序一致
  • 可以存储重复元素

因为List集合可以使用索引,故围绕着索引,设计很多API方法:

1
2
3
4
5
6
7
8
9
10
11
12
//添加元素
List集合.add( 索引 , 元素值) //向List集合中指定索引位置上,添加元素
//如指定索引位置上已有索引,会自动后向移动

//修改元素
List集合.set( 索引 , 元素值) //修改List集合中指定索引位置上的元素值

//删除元素
List集合.remove(索引) //删除List集合中指定索引位置上的元素值

//获取元素
List集合.get(索引);

ArrayList集合:

  • 实现List接口(List接口中的所有功能都有)
  • 底层使用:数组
    • 查询快 、 增删慢

链表结构:

  • 在内存中是使用==节点==来存储数据

    • 节点 = 数据 + 地址
  • 链表:有头、有尾

  • 分类:
    1. 单向链表:只能从头到尾
    2. 双向链表:可以从头到尾,也可以从尾到头 (提高查询效率)
  • 代表集合类:LinkedList

LinkedList集合:

  • 实现List接口

  • 底层使用:双向链表(有头有尾)

    • 增删快 、 查询慢
  • 特有方法都围绕着链表头和尾设计

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //添加元素
    addFirst( 元素 ) //把元素添加到链表头部
    addLast( 元素 ) //把元素添加到链表尾部

    //删除元素
    removeFirst()
    removeLast()

    //获取元素
    getFirst()
    getLast()

Set集合

Collection集合

  • List集合

    1. 有索引
    2. 存取元素有序
    3. 可能存储重复元素
  • Set集合

    1. 没有索引
    2. 存取元素不保证顺序
    3. 不允许存储重复元素

Set集合中方法全部来自Collection集合

Set集合的子类:

  • HashSet集合

    • 特点:

      1. 没有索引
      2. 存取元素不保证顺序
      3. 不能存储重复元素
    • 底层使用:哈希表结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package io.github.ethanliu6.set;

import java.util.HashSet;
import java.util.Objects;

/**
* @Author EthanLiu 16693226842@163.com
* @Date 2024/4/20 20:26
* @Project JavaCode_SE_Advance
* @Theme HashSet
*/
public class HashSetDemo1 {
public static void main(String[] args) {
HashSet<Person> peopleSet = new HashSet<>();

//向集合中添加Person对象
peopleSet.add(new Person("Qiu Zhu", 21)); //匿名对象
peopleSet.add(new Person("Ethan Liu", 21));
peopleSet.add(new Person("Qiu Zhu", 21)); //输出发现并没有去重,需要重写hashCode并用equals方法


//使用增强for来遍历(迭代器)
for (Person person : peopleSet) {
System.out.println(person);
}
}

}

class Person {
private String name;
private int age;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", 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;
}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public Person() {
}
}
  • LinkedHashSet集合
  • TreeSet集合

哈希表数据结构:

  • 底层是使用大小为16的数组+链表组成的存储方式

  • 哈希表存储数据的方式 ( 借助:哈希值[存储位置] )

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    拿要存储的元素,结合哈希算法, 计算出哈希值(存储位置) // 调用:  元素.hashCode()

    判断 : 计算出的存储位置上是否有元素存在
    情况1 : 没有元素存在 => 直接存储
    情况2 : 已有元素存在
    拿要存储的元素 和 已经存在的元素 进行比较(比较内容是否相同) //元素.equals()
    相同: (重复元素) => 不存储
    不相同: (不重复元素)
    再次拿当前存储空间作为算法因子,再次进行哈希算法,计算新的存储空间


  • 从JDK1.8开始,哈希表底层进行优化,使用:数组+链表/红黑树

    • 从链表 => 红黑树时机: 当链表的长度>8,自动 把链表转换为红黑树

Set集合:

  • HashSet

    • 底层:哈希表结构
    • 特点:
      1. 不能存储重复元素
      2. 没有索引
      3. 存取元素顺序不保证一致
  • LinkedHashSet

    • 特点:

      1. 没有索引
      2. 不能存储重复元素
      3. 存取元素顺序一致
    • 底层:哈希表+链表(保证存储元素的顺序)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      package io.github.ethanliu6.set;

      import java.util.LinkedHashSet;

      /**
      * @Author EthanLiu 16693226842@163.com
      * @Date 2024/4/20 21:10
      * @Project JavaCode_SE_Advance
      * @Theme LinkedHashSet
      */
      public class LinkedHashSetDemo1 {
      //验证: 获取元素顺序
      public static void main(String[] args) {
      //创建集合对象
      LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();

      linkedHashSet.add("Qiu");
      linkedHashSet.add("Ethan");
      linkedHashSet.add("Qiu");
      linkedHashSet.add("Liu");

      for (String s : linkedHashSet) {
      System.out.println(s); //有序的
      }
      }
      }
  • TreeSet(下一篇文章)