JAVA集合框架(看完这一篇就够了)


集合概念

什么是集合?

集合:集合是一组用来存放数据对象的容器。
特点:存放的数据是对象。
对比数组:
1、数组初始化后,其长度不可以再被改变,也就是说数组存放数据的个数是事先确定的。
2、数组中对于插入元素和删除元素的操作是很繁琐的,因为涉及到了数组元素的移位操作。
3、数组存放数据的方式是有序的,可重复的,而对于那些需要去掉重复数据的需求就无法满足了。
但是对于数组以上的三点缺陷,在集合中就得到了很好的解决。

集合的分类

集合可分为两大类:单例集合和双例集合。

什么是单列集合?

单例集合:每次以单个对象的形式将数据存放到集合中。

什么是双列集合?

双列集合:每次以两个对象为一组的形式将数据存放到集合中,用来存储键值对对象,其中键具有唯一性,而值是可以重复的。

集合分类示意图

在这里插入图片描述
如上图,左边的Collection接口为单列集合的接口,右边的Map接口为双列集合的接口,下面分别对应他们的子接口以及对应的实现类。


集合详解

一、Collection接口的设计与实现

1、简介

Collection: 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
对于上面用黄色圈出来的字,解释是这样的:
1、对于Collection这个接口,我们如果需要直接调用它的一些方法的话,我们是需要去通过间接的使用Collection它的子接口的实现类来调用Collection中的方法的,如果说,我们手动的定义一个Collection接口的实现类的话我们是需要去重写它的所有方法的,这样就得不偿失了。例如下面:

package Collection;

import java.util.*;

public class Test  implements Collection{
//当我们定义Test类去实现Collection接口时,我们发现此时就需要去重写它的方法,
//这样并不太方便

   @Override
   public Object[] toArray() {
      return new Object[0];
   }

   @Override
   public boolean add(Object o) {
      return false;
   }

   @Override
   public boolean remove(Object o) {
      return false;
   }

   @Override
   public boolean addAll(Collection c) {
      return false;
   }

}

2、如果我们使用它的子接口的实现类去创建对象,调用方法时

public class Test  {
   Collection col = new ArrayList();
   Object O = col.add(5);//

}

此时我们就可以直接调用其方法而不需要去完成重写操作。

2、常见方法

在这里插入图片描述

添加元素操作:

public class Test  {
   public static void main(String[] args) {
      Collection col = new ArrayList();
      col.add(18);
      col.add(20);
      col.add(21);
      System.out.println(col);
   }

}

在这里插入图片描述

遍历操作

在这里插入图片描述

3、子接口的设计与实现

List接口

LIst:有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

用法

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

实现类:ArryList

ArrayList的底层结构:是一个数组,根据索引定位元素快,增加、删除操作需要数组的移位,第一次创建集合并添加一个元素时,底层就会创建一个大小为10的数组。
这里有一个小知识点:
在JDK1.7中:创建集合时,底层就会直接开辟一个大小为10的数组。
在JDK1,7后:创建集合时,底层只是调用了构造方法创建了一个空数组,当往集合中添加一个元素后,调用add方法,此时的集合就指向一个新开辟的大小为10的新数组,这样做就节省了内存。
在这里插入图片描述
上图为实现类在内存中的示意图。
注意:当我们存放的数据大小大于10的时候,这时就会新开辟一个数组,大小为原数组的1.5倍。

实现类:vector

面试题;vector与ArrayList的区别?
1、底层的扩容机制不同:vector扩容为原数组的两倍,而ArrayList扩容为原数组的1.5倍。
2、内部实现机制不同:vector类的操作方法采用的都是synchronize同步处理,线程安全但效率低。ArrayList类的操作方法却并没有采用synchronize同步处理,线程不安全但效率高。

实现类:LinkedList

LinkedList的底层结构:是一个双向链表,可以充分利用碎片化空间进行存储,查找,修改数据的时间复杂度都是O(n),因为都要遍历整个链表。
在这里插入图片描述

面试题:请问ArrayList与LinkedList有什么区别?
1、ArrayList是数组实现的集合结构,LinkedList是双向链表实现的集合结构。
2、LinkedList不需要频繁的开辟新数组空间,而ArrayListx需要,但是ArrayList根据索引查找元素的时间复杂度为O(1),而LinkedList根据索引查找元素的时间复杂度为O(N),很显然在查询操作上前者更加快速。

set接口

用法

在这里插入图片描述
在这里插入图片描述

实现类:HashSet

HashSet的底层结构:数组+链表=哈希表。

什么是哈希表?要理解这个东西,我们首先要搞清楚什么是哈希值

哈希值:是JDK根据对象的地址,按照某种规则计算出来的一个int型的数值。

如何获取哈希值:
通过Object类提供的API

public int hashCodde(); 返回对象的哈希值
1、同一个对象多次调用hashCode方法返回的哈希值是相同的。
2、不同对象调用的hashCode方法返回的哈希值默认是不同的。
在这里插入图片描述
了解了哈希值之后,我们来看看数据是怎么样存入到HashSet集合中的:

1、当我们创建一个HashSet集合的时候,在内存中就会开辟一个大小为16的Entry型的数组,数组名叫table。
2、当我们往集合中添加一个元素时,首先就会调用HashCode()求出该元素的哈希值,求出哈希值之后,除以数组长度然后取余,对应的取余值就是该元素在哈希表中的位置。
3、在存入之前,会判断数组上该位置是否为空,如果为空就直接存入,如果不为空,则进行第4步。
4、调用equals()方法,对两者进行比较,如果值相同,则不存入该元素,如果值不相同,则进行第5步。
5、JDK 7 新元素占用老元素位置,并指向老元素,形成链表结构;
JDK 8 新元素存在老元素下面,即老元素指向新元素。
在这里插入图片描述
注意:如果要放入HashSet元素,一定要重写两个方法:hashCode()、equals()。

实现类:TreeSet

TreeSet的底层结构:红黑树,也就说是一棵自平衡的排序二叉树。

有同学不清楚红黑树的排序原理,下面我们讲一下(以自平衡排序二叉树为例):

1、首先我们需要明白的是二叉树的度是2。
2、其次,红黑树会遵循向上分裂的原则。
在这里插入图片描述
可以根据上图,我们就能简单的理解什么是红黑树了。

讲完了红黑树,我们来讲一下另一个重要的知识点—比较器:
1、内部比较器:自定义定义一个类实现Comparable接口,在类内部重写compareTo方法
2、外部比较器:专门定义一些实现Comparable接口的比较类,然后在主方法中通过创建实现类对象(以多态的形式),通过对象调用比较方法对两个自定义的待比较对象进行比较,结果返回一个整数。

为什么要讲这个知识点?

对于TreeSet集合,他所存入的数据是有序的,既然是有序的,那么它的排序规则是怎么样的?
1、对于数值型:Integer、Double等,系统默认按大小进行升序排序
2、对于字符串:对首字母的编号进行升序排序(abcdefg…)。
3、对于自定义类型的数据:你需要自己自定排序规则。
在这里插入图片描述
这时候比较器的作用不就表现出来了吗?

我们来看看如何使用比较器对自定义数据进行排序:

package Collection;
/*
1、用内部构造器
 */
public class Student implements Comparable<Student>{
    private String name;
    private String id;
    private int age;
    private String sex;


    public Student(String name, String id, int age, String sex) {
        this.name = name;
        this.id = id;
        this.age = age;
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    @Override
    public int compareTo(Student o) {
//按照年龄进行比较
        return this.age- o.age;
    }

}

在这里插入图片描述

package Collection;
/*
使用外部比较器
 */
import java.util.Comparator;

public class Test {

   public static void main(String[] args) {
      Student std1 = new Student("wang","2009",18,"男");
      Student std2 = new Student("li","2010",19,"男");
      Student std3 = new Student("sun","2011",20,"男");
      Student std4 = new Student("liu","2012",21,"女");
      TreeSet set = new TreeSet();
      set.add(std1);
      set.add(std2);
      set.add(std3);
      set.add(std4);
//外部比较器
      TreeSet<Student> set1 = new TreeSet<Student>(new Comparator<Student>() {
         @Override
         public int compare(Student o1, Student o2) {
            return o1.getAge()- o2.getAge();
         }
      });
      System.out.println(set);
   }
}

4、小结

对于Collection接口,它有两个子接口List、set。他们对应的实现类分别为ArrayList、LinkedList、HashSet、TreeSet。他们的区别如下:

    |--List    有序,可重复
        |--ArrayList
            底层数据结构是数组,查询快,增删慢。
            线程不安全,效率高
        |--Vector
            底层数据结构是数组,查询快,增删慢。
            线程安全,效率低
        |--LinkedList
            底层数据结构是链表,查询慢,增删快。
            线程不安全,效率高
    |--Set    无序,唯一
        |--HashSet
            底层数据结构是哈希表。
            如何保证元素唯一性的呢?
                依赖两个方法:hashCode()和equals()
          
        |--TreeSet
            底层数据结构是红黑树。
            如何保证元素排序的呢?
                自然排序
                比较器排序
            如何保证元素唯一性的呢?
                根据比较的返回值是否是0来决定

二、Map接口的设计与实现

1、简介

1、Map集合是一种双列集合,每个元素包含两个数据;
2、Map集合的每个元素格式:key=value(键值对元素);
3、Map集合也称“键值对”集合。

2、常见方法

在这里插入图片描述

3、实现类的设计与实现

HashMap的设计与实现

用法

在这里插入图片描述
在这里插入图片描述

底层结构

HashMap底层结构是哈希表。
扩容:
初始化容量扩充为16
当保存内容的容量扩充超过了与阈值*0.75=12时,就会进行容量的扩充。
在进行扩充的时候HashMap采用的成倍的扩充模式即:每一次扩充两倍(源码用的左移运算符1<<)。
在这里插入图片描述
注意:HashMap的存储方式和HashSet一样,唯一的不同点是,HashMap是把键值作为关键点

TreeMap的设计与实现

用法

在这里插入图片描述
在这里插入图片描述

底层结构

TreeMap的底层结构是二叉红黑树,这个于TreeSet一样,唯一不同的是TreeMap把键值作为关键点。


总结

本章主要介绍了Java集合的基本内容,包括集合的概念、种类、特点、以及相应的实现等,对于几种常见的集合,如何选择呢?
针对Collection集合我们到底使用谁呢?(掌握)
唯一吗?
是:Set
排序吗?
是:TreeSet
否:HashSet
如果你知道是Set,但是不知道是哪个Set,就用HashSet。

    否:List
        要安全吗?
            是:Vector
            否:ArrayList或者LinkedList
                查询多:ArrayList
                增删多:LinkedList
    如果你知道是List,但是不知道是哪个List,就用ArrayList。

如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。

如果你知道用集合,就用ArrayList。

在集合中常见的数据结构(掌握)
ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无树菩提~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值