集合和泛型

1集合

集合是可变的容器,关于容器的具体知识,转移至之前的容器相关文章
在学习集合之前首先理解数组和集合的区别

  • 相同点
    都是容器,可以存储多个数据
  • 不同点
  • 数组的长度是不可变的,集合的长度是可变的
  • 数组可以存基本数据类型和引用数据类型
  • 集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
    集合主要是目前主要理解包括单列集合Collection和双列集合Map集合。

1.1Collection集合

1.11 Collection概述

Collection是单列集合的顶层接口,它表示一组对象,这些对象也被称为Collection的元素

  • 创建Collection集合对象的实现方式
    • 多态的方式,Collection指向子类如,new ArrayList() 或new LinkList等
  • Collection集合常用方法
方法名说明
boolean add(E e)添加元素
boolean remove(Object o)从集合中移除指定的元素
boolean removeIf(Object o)根据条件进行移除
void clear()清空集合中的元素
boolean contains(Object o)判断集合中是否存在指定的元素
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中元素的个数

1.2迭代器概述

前面讲解循环章节的时候,提到过迭代器是集合专用的遍历方式。

  • 迭代器的原理
    • 迭代器主要方法是hasNext()方法判断当前位置是否有元素,next() 方法取出当前元素,指针向下移动一个位置来配合来使用。创建的迭代器对象是临时的,复制之前集合的。因此迭代器用于遍历集合,而不是删除。增强for的底层也是基于迭代器来实现的,因此也可以使用增强for来遍历集合。
  • 迭代器使用条件
    • 使用的类必须实现Iterable接口或接口继承Iterable其本身和子类才能使用迭代器进行遍历。
      通过API文档可以发现,单列集合Collection继承了Iterable,因此单列集合都可以直接使用迭代器和增强for。双列集合Map没有继承Iterable接口,因此不能直接使用迭代器。

1.3 List集合

Collection集合下方的子接口包括List集合和Set集合。

1.31List集合概述

  • List集合的特点
    • List集合是有序集合,这里指的是存取顺序
    • List集合的元素允许重复
    • List集合有索引,可以通过索引操作List里的元素
方法名描述
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素

1.32List集合的实现类ArrayList和LinkList

ArrayList底层数据结构是数组,LinkList底层数据结构是双向链表。

  • ArrayList和LinkList的区别,通过二者的数据结构不难发现区别
    • ArrayList查询速度快,增删速度慢
    • LinkList查询速度慢,增删速度块
方法名说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

1.4Set集合

1.41Set集合概述

  1. 不能存储重复元素
  2. 没有索引,不能使用普通for遍历
  3. 存取顺序可能不一致
  4. 底层数据结构是红黑树(JDK1.8以后)

1.42TreeSet集合

TreeSet是Set集合的实现类,因此Set有的特征,TreeSet也有,可以知道TreeSet集合没有索引,元素不能重复,存取顺序不一致。TreeSet根据指定的比较器进行排序

1.42TreeSet集合的两种比较器

自然排序和比较器排序比较的返回值都是Int,返回结果为负数当前对象排在左边,结果为0则两个元素为重复元素,舍弃。结果为正数,当前对象排在右边。

  • 1.自然排序Comparable

    • 创建TreeSet空参对象
    • 自定义的类实现Comparable接口,重写CompareTo(T o)方法
    • 重写接口中的compareTo方法要按照主要条件和次要条件进行排序
  • 代码实现

    学生类

    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;
        }
    
        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 "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        @Override
        public int compareTo(Student o) {
            //按照对象的年龄进行排序
            //主要判断条件: 按照年龄从小到大排序
            int result = this.age - o.age;
            //次要判断条件: 年龄相同时,按照姓名的字母顺序排序
            result = result == 0 ? this.name.compareTo(o.getName()) : result;
            return result;
        }
    }
    

    测试类

    public class MyTreeSet2 {
        public static void main(String[] args) {
            //创建集合对象
            TreeSet<Student> ts = new TreeSet<>();
    	    //创建学生对象
            Student s1 = new Student("zhangsan",28);
            Student s2 = new Student("lisi",27);
            Student s3 = new Student("wangwu",29);
            Student s4 = new Student("zhaoliu",28);
            Student s5 = new Student("qianqi",30);
    		//把学生添加到集合
            ts.add(s1);
            ts.add(s2);
            ts.add(s3);
            ts.add(s4);
            ts.add(s5);
    		//遍历集合
            for (Student student : ts) {
                System.out.println(student);
            }
        }
    }
    
  • 2.比较器排序Compartor

    • 用TreeSet带参构造创建对象
    • 比较器排序,就是让TreeSet集合的构造方法接收Comparator的实现类对象(匿名内部类),重写Compare(T 01,T 02)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
  • 代码实现

老师类

public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.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;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类

public class MyTreeSet4 {
    public static void main(String[] args) {
      	//创建集合对象
        TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                //o1表示现在要存入的那个元素
                //o2表示已经存入到集合中的元素
              
                //主要条件
                int result = o1.getAge() - o2.getAge();
                //次要条件
                result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });
		//创建老师对象
        Teacher t1 = new Teacher("zhangsan",23);
        Teacher t2 = new Teacher("lisi",22);
        Teacher t3 = new Teacher("wangwu",24);
        Teacher t4 = new Teacher("zhaoliu",24);
		//把老师添加到集合
        ts.add(t1);
        ts.add(t2);
        ts.add(t3);
        ts.add(t4);
		//遍历集合
        for (Teacher teacher : ts) {
            System.out.println(teacher);
        }
    }
}
  • TreeSet集合的两种比较器总结
    • 自然排序:自定义实现Comparable接口,重写compareTo方法,根据返回值进行排序
    • 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
    • 在使用的时候,默认使用自然排序,当自然排序不满足现在需求时,就必须使用比较器排序、
  • 两个方式中关于返回值的规则
    • 如果返回值为负数,表示当前存入的元素是较小值,存左边
    • 如果返回值为0,表示当前存入的元素跟集合中的元素重复,不存进集合
    • 如果返回值为正数,表示当前存入的元素是较大值,存右边

1.43HashSet集合

1.431HashSet集合概述
  • 底层数据结构是哈希表
  • 存取无序
  • 不可以存储重复元素
  • 没有索引,不能使用普通for循环遍历
1.432哈希值
  • 哈希值简介

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

  • 如何获取哈希值
    ​ Object类中的public int hashCode():返回对象的哈希码值

  • 哈希值的特点

    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
    • 我们知道equals比较两个对象的地址是否相同,因此对于引用类型,equals相同,hashCode一定相同。而HashCode相同,equals不一定相同。

哈希表结构

创建一个默认长度为16,默认加载因子0.75的数组,这是默认的哈希表。当哈希值相同的不同元素,会采用头插法挂在对应数组下方形成链表。jdk1.8及以后链表超过8,采用红黑树。
总结:

  • JDK1.8以前

    ​ 数组 + 链表
    在这里插入图片描述

  • JDK1.8以后

    • 节点个数少于等于8个
      ​ 数组 + 链表
    • 节点个数多于8个
      ​ 数组 + 红黑树
      在这里插入图片描述
      总结
      ​ HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法

2泛型

  • 泛型的介绍
    • 泛型是JDK5中引入的特性,它提供了编译时类型安全检查机制
  • 泛型的好处
    • 1.把运行时期的问题提前到了编译时期
    • 2.避免了强制类型转换
  • 泛型的定义格式
    • <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如:
    • <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如: <E,T> <K,V>

2.1泛型应用

泛型用于主要类,接口,方法 ,分别称为泛型类、泛型接口和泛型方法。

2.11泛型类

  • 定义格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名) {  }
  • 示例:
public class Generic<T> {
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
 

2.12泛型方法

  • 定义格式

    修饰符 <类型> 返回值类型 方法名(类型 变量名) {  }
    

注意:在方法返回类型前面加上泛型声明,告诉方法中的参数这是一个方法,而不是一个类名叫T.如果在接口中或者方法上面已近声明了,则可以不用在返回值前处声明。

  • 示例
  • 定义格式
public class Generic {
    public <T> void show(T t) {
        System.out.println(t);
    }
}

2.13泛型接口

  • 定义格式

    修饰符 interface 接口名<类型> {  }
    
  • 示例:

    • 泛型接口

      public interface Generic<T> {
          void show(T t);
      }
      
  • 泛型接口实现类1

    ​ 定义实现类时,定义和接口相同泛型,创建实现类对象时明确泛型的具体类型

    public class GenericImpl1<T> implements Generic<T> {
        @Override
        public void show(T t) {
            System.out.println(t);
        }
    }
    
  • 泛型接口实现类2

定义实现类时,直接明确泛型的具体类型,实现的接口的类就变成一个普通即可

public class GenericImpl2 implements Generic<Integer>{
     @Override
     public void show(Integer t) {
          System.out.println(t);
     }
}

2.2类型通配符

类型的作用,不用声明这是一个泛型,直接用。其二,类型通配符可以表示范围。

  • 类型通配符上限: <? extends 类型>

    • ArrayListList <? extends Number>: 它表示的类型是Number或者其子类型
  • 类型通配符下限: <? super 类型>

    • ArrayListList <? super Number>: 它表示的类型是Number或者其父类型
  • 泛型通配符的使用

    public class GenericDemo4 {
        public static void main(String[] args) {
            ArrayList<Integer> list1 = new ArrayList<>();
            ArrayList<String> list2 = new ArrayList<>();
            ArrayList<Number> list3 = new ArrayList<>();
            ArrayList<Object> list4 = new ArrayList<>();
    
            method(list1);
            method(list2);
            method(list3);
            method(list4);
    
            getElement1(list1);
            getElement1(list2);//报错
            getElement1(list3);
            getElement1(list4);//报错
    
            getElement2(list1);//报错
            getElement2(list2);//报错
            getElement2(list3);
            getElement2(list4);
        }
      
        // 泛型通配符: 此时的泛型?,可以是任意类型
        public static void method(ArrayList<?> list){}
        // 泛型的上限: 此时的泛型?,必须是Number类型或者Number类型的子类
        public static void getElement1(ArrayList<? extends Number> list){}
        // 泛型的下限: 此时的泛型?,必须是Number类型或者Number类型的父类
        public static void getElement2(ArrayList<? super Number> list){}
    
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值