黑马程序员——javase进阶——day05——泛型,数据结构,List,Set集合

目录:

  1. 泛型
    1. 泛型的介绍
    2. 使用泛型的好处
    3. 泛型的注意事项
    4. 自定义泛型类
    5. 自定义泛型接口
    6. 自定义泛型方法
    7. 通配符
    8. 受限泛型
  2. 数据结构
  3. List集合
  4. Set集合
  5. 随堂小计
    1. BigDecimal类存在的问题:精度丢失
    2. 迭代器的使用步骤:
    3. 泛型
    4. List集合

1.泛型

泛型的介绍
  • 泛型是一种类型参数,专门用来保存类型用的

    • 最早接触泛型是在ArrayList<E>,这个E就是所谓的泛型了。使用ArrayList时,只要给E指定某一个类型,里面所有用到泛型的地方都会被指定对应的类型

使用泛型的好处
  • 不用泛型带来的问题

    • 集合若不指定泛型,默认就是Object。存储的元素类型自动提升为Object类型。获取元素时得到的都是Object,若要调用特有方法需要转型,给我们编程带来麻烦.

  • 使用泛型带来的好处

    • 可以在编译时就对类型做判断,避免不必要的类型转换操作,精简代码,也避免了因为类型转换导致的代码异常

 //泛型没有指定类型,默认就是Object
ArrayList list = new ArrayList();
list.add("Hello");
list.add("World");
list.add(100);
list.add(false);
//集合中的数据就比较混乱,会给获取数据带来麻烦
for (Object obj : list) {
    String str = (String) obj;
    //当遍历到非String类型数据,就会报异常出错
    System.out.println(str + "长度为:" + str.length());
}
泛型的注意事项
  • 泛型在代码运行时,泛型会被擦除。后面学习反射的时候,可以实现在代码运行的过程中添加其他类型的数据到集合
  • 泛型只在编译时期限定数据的类型 , 在运行时期会被擦除
自定义泛型类
  • 当一个类定义其属性的时候,不确定具体是什么类型时,就可以使用泛型表示该属性的类型

  • 定义的格式

    • 在类型名后面加上一对尖括号,里面定义泛型。一般使用一个英文大写字母表示,如果有多个泛型使用逗号分隔

    • public class 类名<泛型名>{ ... }

    • 举例 : 
      public class Student<X,Y>{  
          X  xObj; 
      } 
    • 泛型的确定

      • 当创建此泛型类是 , 确定泛型类中泛型的具体数据类型

    • 练习

    • package com.itheima.genericity_demo.genericity_class;
      
      import java.time.Period;
      
      /*
          需求 : 定义一个人类,定义一个属性表示爱好,但是具体爱好是什么不清楚,可能是游泳,乒乓,篮球。
       */
      public class GenericityDemo {
          public static void main(String[] args) {
              Person<BasketBall> person = new Person<>();
              person.setHobby(new BasketBall());
      
              Person<Swim> person2 = new Person<>();
              person2.setHobby(new Swim());
      
              Person person3 = new Person<>();// 如果没有指定泛型 , 那么默认使用Object数据类型
          }
      }
      
      class Person<H> {
          // 定义属性表达爱好
          private H hobby;
      
      
          public H getHobby() {
              return hobby;
          }
      
          public void setHobby(H hobby) {
              this.hobby = hobby;
          }
      }
      
      class Swim {
      }
      
      class PingPang {
      }
      
      class BasketBall {
      }
自定义泛型接口
  • 当定义接口时,内部方法中其参数类型,返回值类型不确定时,就可以使用泛型替代了。

  • 定义泛型接口

    • 在接口后面加一对尖括号 , 尖括号中定义泛型 , 一般使用大写字母表示, 多个泛型用逗号分隔

    • public interface<泛型名> { ... }

    • 举例 :

    • public interface Collection<E>{
      	public boolean add(E e);
      } 
    • 泛型的确定

      • 实现类去指定泛型接口的泛型

      • 实现了不去指定泛型接口的泛型 , 进行延续泛型 , 回到泛型类的使用

package com.itheima.genericity_demo.genericity_interface;
/*
    需求:
    模拟一个Collection接口,表示集合,集合操作的数据不确定。
    定义一个接口MyCollection具体表示。

 */
// 泛型接口
public interface MyCollection<E> {
    // 添加功能
    public abstract void add(E e);
    // 删除功能
    public abstract void remove(E e);
}

// 指定泛型的第一种方式 : 让实现类去指定接口的泛型
class MyCollectionImpl1 implements MyCollection<String>{
    @Override
    public void add(String s) {

    }
    @Override
    public void remove(String s) {

    }
}
// 指定泛型的第二种方式 : 实现类不确定泛型,延续泛型,回到泛型类的使用
class MyCollectionImpl2<E> implements MyCollection<E>{

    @Override
    public void add(E a) {

    }

    @Override
    public void remove(E a) {

    }
}

 

自定义泛型方法
  • 当定义方法时,方法中参数类型,返回值类型不确定时,就可以使用泛型替代了

  • 泛型方法的定义

    • 可以在方法的返回值类型前定义泛型

    • 格式 : public <泛型名> 返回值类型 方法名(参数列表){ ... }

    • 举例 : public <T> void show(T t) { ... }

  • 泛型的确定

    • 当调用一个泛型方法 , 传入的参数是什么类型, 那么泛型就会被确定

  • 练习

package com.itheima.genericity_demo.genericity_method;

import java.util.ArrayList;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        // Collection集合中 : public <T> T[] toArray(T[] a) : 把集合中的内容存储到一个数组中 , 进行返回
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("ads");
        list.add("qwe");
        String[] array = list.toArray(new String[list.size()]);
        System.out.println(Arrays.toString(array));
    }

    // 接收一个集合 , 往集合中添加三个待指定类型的元素
    public static <X> void addElement(ArrayList<X> list, X x1, X x2, X x3) {
        list.add(x1);
        list.add(x2);
        list.add(x3);
    }
}

 

通配符
  • 当我们对泛型的类型确定不了,而是表达的可以是任意类型,可以使用泛型通配符给定

    符号就是一个问号:? 表示任意类型,用来给泛型指定的一种通配值。如下

public static void shuffle(List<?> list){
   //…
} 

说明:该方法时来自工具类Collections中的一个方法,用来对存储任意类型数据的List集合进行乱序

泛型通配符结合集合使用

  • 泛型通配符搭配集合使用一般在方法的参数中比较常见。在集合中泛型是不支持多态的,如果为了匹配任意类型,我们就会使用泛型通配符了。

  • 方法中的参数是一个集合,集合如果携带了通配符,要特别注意如下

    • 集合的类型会提升为Object类型

    • 方法中的参数是一个集合,集合如果携带了通配符,那么此集合不能进行添加和修改操作 , 可以删除和获取

package com.itheima.genericity_demo;


import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("asd");
        list.add("qwe");
        // 方法的参数是一个集合 , 集合的泛型是一个通配符 , 可以接受任意类型元素的集合
        show(list);
    }

    public static void show(List<?> list) {
        // 如果集合的泛型是一个通配符 , 那么集合中元素以Object类型存在
        Object o = list.get(0);

        // 如果集合的泛型是一个通配符 , 那么此集合不能进行添加和修改操作 , 可以删除和获取
        // list.add(??);

        // 删除可以
        list.remove(0);
        // 获取元素可以
        for (Object o1 : list) {
            System.out.println(o1);
        }
    }
}
package com.itheima.genericity_demo;

import java.util.ArrayList;

/*
    已知存在继承体系:Integer继承Number,Number继承Object。
    定义一个方法,方法的参数是一个ArrayList。
    要求可以接收ArrayList<Integer>,ArrayList<Number>,ArrayList<Object>,ArrayList<String>这些类型的数据。

    结论 : 具体类型的集合,不支持多态 , 要想接收任意类型集合 , 需要使通配符集合
 */
public class Test1 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<Number> list2 = new ArrayList<>();
        ArrayList<String> list3 = new ArrayList<>();
        ArrayList<Object> list4 = new ArrayList<>();

        useList5(list1);
        useList5(list2);
        useList5(list3);
        useList5(list4);
    }

    // 此方法只能接收存储Integer类型数据的集合
    public static void useList1(ArrayList<Integer> list) {

    }

    // 此方法只能接收存储Number类型数据的集合
    public static void useList2(ArrayList<Number> list) {

    }

    // 此方法只能接收存储String类型数据的集合
    public static void useList3(ArrayList<String> list) {

    }

    // 此方法只能接收存储Object类型数据的集合
    public static void useList4(ArrayList<Object> list) {

    }

    public static void useList5(ArrayList<?> list) {

    }

}
受限泛型
  • 受限泛型是指,在使用通配符的过程中 , 对泛型做了约束,给泛型指定类型时,只能是某个类型父类型或者子类型

  • 分类 :

    • 泛型的下限 :

      • <? super 类型> //只能是某一类型,及其父类型,其他类型不支持

    • 泛型的上限 :

      • <? extends 类型> //只能是某一个类型,及其子类型,其他类型不支持

package com.itheima.genericity_demo.wildcard_demo;

import java.util.ArrayList;

/*
  wildcardCharacter

  基于上一个知识点,定义方法
    show1方法,参数只接收元素类型是Number或者其父类型的集合
    show2方法,参数只接收元素类型是Number或者其子类型的集合

 */
public class Test2 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<Number> list2 = new ArrayList<>();
        ArrayList<Object> list3 = new ArrayList<>();

        show1(list3);
        show1(list2);

        show2(list2);
        show2(list1);
    }
    // 此方法可以接受集合中存储的是Number或者Number的父类型 , 下限泛型
    public static void show1(ArrayList<? super Number> list) {

    }
    // 此方法可以接受集合中存储的是Number或者Number的子类型 , 上限泛型
    public static void show2(ArrayList<? extends Number> list) {

    }
}

 

2.数据结构

  • 栈结构 : 先进后出

  • 队列结构 : 先进先出

  • 数组结构 : 查询快 , 增删慢

  • 链表结构 : 查询慢 , 增删快

  • 二叉树

    • 二叉树 : 每个节点最多有两个子节点

    • 二茬查找树 : 每个节点的左子节点比当前节点小 , 右子节点比当前节点大

    • 二茬平衡树 : 在查找树的基础上, 每个节点左右子树的高度不超过1

    • 红黑树 :

      • 每一个节点或是红色的,或者是黑色的

      • 根节点必须是黑色

      • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的

      • 不能出现两个红色节点相连的情况

      • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

      • 添加元素 :

  • 哈希表结构 :

    • 哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

    • Object类中有一个方法可以获取对象的哈希值 public int hashCode():返回对象的哈希码值

    • 对象的哈希值特点

      • 同一个对象多次调用hashCode()方法返回的哈希值是相同的

      • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同

 

3.List集合

  • List集合是Collection集合子类型,继承了所有Collection中功能,同时List增加了带索引的功能

  • 特点 :

    • 元素的存取是有序的【有序】

    • 元素具备索引 【有索引】

    • 元素可以重复存储【可重复】

  • 常见的子类

    • ArrayList:底层结构就是数组【查询快,增删慢】

    • Vector:底层结构也是数组(线程安全,同步安全的,低效,用的就少)

    • LinkedList:底层是链表结构(双向链表)【查询慢,增删快】

  • List中常用的方法

    • public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。

    • public E get(int index):返回集合中指定位置的元素

    • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。\

    • public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素

  • LinkedList类

    • LinkedList底层结构是双向链表。每个节点有三个部分的数据,一个是保存元素数据,一个是保存前一个节点的地址,一个是保存后一个节点的地址。可以双向查询,效率会比单向链表高。

    • LinkedList特有方法

      • public void addFirst(E e):将指定元素插入此列表的开头。

      • public void addLast(E e):将指定元素添加到此列表的结尾。

      • public E getFirst():返回此列表的第一个元素。

      • public E getLast():返回此列表的最后一个元素。

      • public E removeFirst():移除并返回此列表的第一个元素。

      • public E removeLast():移除并返回此列表的最后一个元素。

4.Set集合

  • Set集合也是Collection集合的子类型,没有特有方法。Set比Collection定义更严谨

  • 特点 :

    • 元素不能保证插入和取出顺序(无序)

    • 元素是没有索引的(无索引)

    • 元素唯一(元素唯一)

  • Set常用子类

    • HashSet:底层由HashMap,底层结构哈希表结构。 去重,无索引,无序。 哈希表结构的集合,操作效率会非常高。

    • LinkedHashSet:底层结构链表加哈希表结构。 具有哈希表表结构的特点,也具有链表的特点。

    • TreeSet:底层是有TreeMap,底层数据结构 红黑树。 去重,让存入的元素具有排序(升序排序)

5.随堂小计 

BigDecimal类存在的问题:精度丢失
BigDecimal num = new BigDecimal( 100 ); //会有精度丢失的问题

BigDecimal 大浮点对象 = new BigDecimal( "100" ); //推荐使用方式


BigDecimal  返回值 = 大浮点对象.add( BigDecimal 参数 )
迭代器的使用步骤:
//1、通过集合对象,获取迭代器对象
Iterator  it  = 集合对象.iterator();

//2、使用迭代器对象中的API方法,获取集合中的每一个元素
while( it.hasNext() ){
    //取出每一个元素
    Object obj = it.next()
}

在开发中为了方便程序员的书写,把迭代器进行简化: 增强for

//增强for底层 :还是迭代器

for(元素类型 变量 : 集合){
    
}
泛型

泛型是什么?

在java语言中,是一种类型参数,可以设置存储数据的类型

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

  • 在创建集合对象时,明确了集合中所存储元素的类型(限定类型)
  • 泛型在使用在代码编写时期的技术方式(编译期技术)
  • 泛型在程序运行后,就擦除

泛型的使用

  • 泛型类

public class 类名<T>{
    
}

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

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

泛型类<String> 对象 = new 泛型类<String>();//泛型类中成员变量的类型为:String
  • 泛型接口
public class 接口名<T>{
    
}

//当不确定接口中的某个方法参数使用什么类型、或方法的返回值使用什么类型时:可以用泛型表示
public interface 泛型接口<T>{
    public void method(T a);
}

//情况1:在子类编写时,指定接口上泛型的具体类型
public class 子类 implements 泛型接口<String>{
    //方法重写
    public void method(String a){
        
    }
}
//情况2:在子类编写时,没有指定接口上的泛型。 意味着:子类也使用和接口相同的泛型了(子类:泛型类)
public class 子类<T> implements 泛型接口<T>{
    //方法重写
    public void method(T a){
        
    }
}
子类<Integer> 对象 = new 子类<>();//创建子类对象时,明确了泛型的类型
  •  泛型方法
//语法格式
修饰符号 <泛型> 返回值类型 方法名( 泛型 参数1 , ...){
    //方法体
}

//当前类没有声明为泛型类,但该类中的方法参数或方法返回值不确定类型时: 使用泛型方法
public <T> void method(T param){
    
}
//当调用方法时,向方法中传递参数的类型,就是泛型的类型

 

泛型中的通配符:  ?   (任意类型)    

通常在开发中,?是和泛型的上下限一起使用

泛型的下限

//指定泛型中的最小类型
<? super 最小类型>
    
? 可以是最小类型
? 可以是父类型    

泛型的上限

//指定泛型中的最大类型
<? extends 最大类型>
    
? 可以是最大类型
? 可以是子类型   

你们认为什么是数据结构?

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

常见的数据结构:

  1. 栈(先进后出)

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

  3. 链表

  4. 哈希表

    • 二叉树

    • 平衡二叉树

    • 红黑树

队列数据结构: (队列是一种线性结构)

  • 特点:先进先出

  • 数组

回顾上午内容:

泛型

泛型是什么

泛型是一种类型参数,可以设置存储的类型

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

在创建集合时,使用泛型约束所存储的元素类型。好处:在使用时不需要强制转换

泛型怎么使用

//泛型在使用时的划分: 泛型类、泛型接口、泛型方法

//泛型类: 当类中的某个属性类型不确定时,可以使用泛型来表示
public class 类名<E,T>{
    private String name;
    private E param;//不确定属性的类型
    
    public void method(T arg){       
    }
}
类名<String,Integer> 对象 = new 类名<>();
对象.method( Integer类型的参数 );


//泛型接口: 当接口中某个方法的参数或返回值不确定类型时,可以使用泛型来表示
public interface 接口名<T>{
    public void method(T param);
    public T show(T args);
}
class 子类 implements 接口名<String>{
    //子类在实现接口时,明确了接口上泛型的类型(String类型)
}
class 子类<T> implements 接口名<T>{
    //子类继续延伸使用接口上的泛型
    //子类就变为: 泛型类
}


//泛型方法(提前:非泛型类):当类中的某个方法的参数不确定或返回值不确定时,使用泛型方法
public class 类名{
    //泛型方法
    public <T> void 方法名(T 参数名){
        
    }
    public static <T> T 方法名(T 参数名){
        
    }
}
String 结果 = 类名.方法名( String类型 ) 

泛型在使用中的注意细节

  • 泛型不支持多太态

ArrayList<Person> list = new ArrayList<Student>(); //报错 

  • 泛型在语法上支持:通配符 ? //表示任意类型  

ArrayList<?> list = new ArrayList<Student>(); 

  • 泛型中的通配符,通常会配置上下限使用 

//泛型上下限,通常用于方法定义时

//上限
public void method(ArrayList<? extends 父类型>  list){
    //传递的list集合对象中存储的元素必须是:父类型 或 其子类型
}

//下限
public void method(ArrayList<? super 子类型>  list){
    //传递的list集合对象中存储的元素必须是:子类型 或 其父类型
}

 

  • 数据结构:栈、队列  

什么是数据结构?
存储数据时,底层对数据排列的方式

常见数据结构: 栈、队列、数组、链表、哈希表、树

计算机底层最原始的数据结构仅有两种: 线性结构、非线性结构

栈特点:先进后出
队列特点:先进先出

数组结构:

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

  • 查询快

  • 增删元素效率慢

  • 在已学习的ArrayList集合,底层就是使用:数组结构

ArrayList集合的特点:查询快、增删慢

List集合

Java语言中集合体系划分:

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

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

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

java.util.List集合:

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

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

//添加元素
List集合.add( 索引 , 元素值) //向List集合中指定索引位置上,添加元素
                           //如指定索引位置上已有索引,会自动后向移动

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

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

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

 

HashMap集合

ArrayList集合:

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

LinkedList集合:

  • 实现List接口 
  • 底层使用:双向链表(有头有尾)
    • 增删快 、 查询慢
  • 特有方法都围绕着链表头和尾设计
//添加元素
addFirst( 元素 ) //把元素添加到链表头部
addLast( 元素 )  //把元素添加到链表尾部    


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


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

链表结构:

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

    • 节点 = 数据 + 地址

  • 链表:有头、有尾

  • 分类:

    1. 单向链表:只能从头到尾

    2. 双向链表:可以从头到尾,也可以从尾到头 (提高查询效率)

  • 代表集合类:LinkedList

Collection集合

  • List集合

    1. 有索引

    2. 存取元素有序

    3. 可能存储重复元素

  • Set集合

    1. 没有索引

    2. 存取元素不保证顺序

    3. 不允许存储重复元素

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

Set集合的子类:

  • HashSet集合

    • 特点:

      1. 没有索引

      2. 存取元素不保证顺序

      3. 不能存储重复元素

    • 底层使用:哈希表结构

  • LinkedHashSet集合

  • TreeSet集合

哈希表数据结构:

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

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

拿要存储的元素,结合哈希算法, 计算出哈希值(存储位置) // 调用:  元素.hashCode()

判断 : 计算出的存储位置上是否有元素存在    
       情况1 : 没有元素存在     =>  直接存储
       情况2 : 已有元素存在
               拿要存储的元素 和 已经存在的元素 进行比较(比较内容是否相同) //元素.equals() 
               相同: (重复元素)  =>  不存储
               不相同: (不重复元素)
                      再次拿当前存储空间作为算法因子,再次进行哈希算法,计算新的存储空间
  • 从JDK1.8开始,哈希表底层进行优化,使用:数组+链表/红黑树
  • 从链表 => 红黑树时机:  当链表的长度>8,自动 把链表转换为红黑树

Set集合:

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

 

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿瞒有我良计15

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值