JAVA Collection、List、Set、Queue

JAVA Collection、List、Set、Queue

Collection是List、Queue和Set的根接口,通常不被直接使用。
Collection的子接口是List、Set、Queue。
Collection主要是用来定义一些通用方法

  • public boolean add(E e) 把给定的对象添加到当前集合中。
  • public void clear() 清空集合中所有的元素。
  • public boolean remove(E e) 把给定的对象在当前集合中册除。
  • public boolean contains(E e) 判断当前集合中是否包合给定的对象。
  • public boolean isEmpty() 判断当前集合是否为空。
  • public int size() 返回集合中元素的个数。
  • public Object[] toArray() 把集合中的元素,存储到数组中。
    在这里插入图片描述
    下面我们分别介绍一下这三个子接口和map接口

List

List 是一个元素有序的、可以重复、可以为 null 的集合(有时候我们也叫它“序列”)。
Java 集合框架中最常使用的几种 List 实现类是 ArrayList,LinkedList 和 Vector。在各种 List 中,最好的做法是以 ArrayList 作为默认选择。 当插入、删除频繁时,使用 LinkedList,Vector 总是比 ArrayList 快,所以要尽量避免使用它

但是为什么List可以有序和重复呢,其实归根结底都是因为他的底层接口是数组结构,使用数组会在内存中开辟一块连续的内存空间,然后将索引和空间地址一一对应,但是这么做的后果就是,让我们的插入和删除损失了一定的效率,因为每当我们想要从中间插入一个元素时,后面的所有元素都需要进行移动
在这里插入图片描述

所以List是一个有序的、可以有重复元素 的,可以为null、查找快、插入删除慢、线程不安全的集合。

List集合是有索引的,所以List除了继承了Collection的通用方法外还有一些自己独有的针对索引的方法,比如get、set、subList等,具体的需要的朋友可以自行查找一下,这里不再过多赘述。
因为有索引,所以我们也可以直接通过普通的for循环去遍历它

List的主要实现类有ArrayList、LinkedList、vector

ArrayList

ArrayList类是一个特殊的数组–动态数组。通过添加和删除元素,就可以动态改变数组的长度。

Array和数组最主要的区别就是没有长度限制

容量固定时优先使用数组,容纳类型更多,更高效。在容量不确定的情景下, ArrayList更有优势。

LinkedList

LinkedList的底层是一个双向链表,所以没有所谓的扩容机制,删除和添加元素比较快速,但是相对的查找元素会慢很多,因为我们每次查找元素都需要去遍历这个链表。

所以LinkedList是一个动态的、增删快、查找慢、线程不安全的类型

Vecotr

Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的:

  • Vector 是同步访问的。线程安全
  • Vector 包含了许多传统的方法,这些方法不属于集合框架。
    创建了一个向量类的对象后,可以往其中随意插入不同类的对象,即不需顾及类型、也不需预先选定向量的容量,并可以方便地进行查找。

对于预先不知或者不愿预先定义数组大小,并且需要频繁地进行查找,插入,删除工作的情况,可以考虑使用向量类。

Vector类中主要成员字段如下:

  • elementData:是一个Object类型的数组索引,在构造方法中会通过new方式、或toArray()方式创建存储空间。在向Vertor中添加对象过程中,如果存储空间满,会自动扩充空间。
  • elementCount:记录当前Vector中实际保存的对象个数。size()方法返回的就是本字段值。
  • capacityIncrement:当Vector中存储空间占用完毕后,通过本参数指定每次扩充的扩充量。当capacityincrement为0的时候,则每次扩充一倍。

Set

是一个不允许出现重复元素,并且无序的集合,主要有HashSet和TreeSet两大实现类。

在判断重复元素的时候,Set集合会调用hashCode()和equal()方法来实现。

HashSet是哈希表结构,主要利用HashMap的key来存储元素,计算插入元素的hashCode来获取元素在集合中的位置;

TreeSet是红黑树结构,每一个元素都是树中的一个节点,插入的元素都会进行排序;

HashSet

HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。底层数据结构是哈希表。

哈希表
一个元素为链表的数组,综合了数组与链表的优点。

要保证元素唯一性,必须重写元素的hashcode()方法和euqals()方法
在这里插入图片描述

HashSet具有以下特点:

不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也可能发生变化;
HashSet不是同步的;
集合元素值可以是null;

HashSet是线程不安全的,多个线程同时操作时,需要通过代码达到同步

HashSet实现Set接口,底层由HashMap(后面讲解)来实现,为哈希表结构,新增元素相当于HashMap的key,value默认为一个固定的Object。在我看来,HashSet相当于一个阉割版的HashMap;

当有元素插入的时候,会计算元素的hashCode值,将元素插入到哈希表对应的位置中来;

它继承于AbstractSet,实现了Set, Cloneable, Serializable接口。

(1)HashSet继承AbstractSet类,获得了Set接口大部分的实现,减少了实现此接口所需的工作,实际上是又继承了AbstractCollection类;

(2)HashSet实现了Set接口,获取Set接口的方法,可以自定义具体实现,也可以继承AbstractSet类中的实现;

(3)HashSet实现Cloneable,得到了clone()方法,可以实现克隆功能;

(4)HashSet实现Serializable,表示可以被序列化,通过序列化去传输,典型的应用就是hessian协议。

hashSet内部存储机制
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。如果有两个元素通过equals方法比较true,但它们的hashCode方法返回的值不相等,HashSet将会把它们存储在不同位置,依然可以添加成功。
也就是说。HashSet集合判断两个元素的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。

如果想要让相同值的元素只添加一次,靠元素重写hashCode方法和equals方法来判断两个元素是否相等,如果相等则覆盖原来的元素,依此来确保元素的唯一性

为什么不直接使用数组,而用HashSet呢?
因为数组的索引是连续的而且数组的长度是固定的,无法自由增加数组的长度。而HashSet就不一样了,HashCode表用每个元素的hashCode值来计算其存储位置,从而可以自由增加HashCode的长度,并根据元素的hashCode值来访问元素。而不用一个个遍历索引去访问,这就是它比数组快的原因。

HashSet检查重复的原理
在向HashMap中添加元素时,先判断key的hashCode值是否相同,如果相同,则调用equals()、==进行判断,若相同则覆盖原有元素;如果不同,则直接向Map中添加元素;

LinkedHashSet

LinkedHashSet继承于HashSet
LinkedHashSet底层由哈希表和链表两种数据结构完成,具有可预测的迭代次序
也就是说我们存进去的顺序和取出来的顺序是一致的
由哈希表保证元素的唯一性

TreeSet

从名字上可以看出,此集合的实现和树结构有关。与HashSet集合类似,TreeSet也是基于Map来实现,具体实现TreeMap(后面讲解),其底层结构为红黑树(特殊的二叉查找树);

与HashSet不同的是,TreeSet具有排序功能,分为自然排序(123456)和自定义排序两类,默认是自然排序;在程序中,我们可以按照任意顺序将元素插入到集合中,等到遍历时TreeSet会按照一定顺序输出–倒序或者升序;

它继承AbstractSet,实现NavigableSet, Cloneable, Serializable接口。

(1)与HashSet同理,TreeSet继承AbstractSet类,获得了Set集合基础实现操作;

(2)TreeSet实现NavigableSet接口,而NavigableSet又扩展了SortedSet接口。这两个接口主要定义了搜索元素的能力,例如给定某个元素,查找该集合中比给定元素大于、小于、等于的元素集合,或者比给定元素大于、小于、等于的元素个数;简单地说,实现NavigableSet接口使得TreeSet具备了元素搜索功能;

(3)TreeSet实现Cloneable接口,意味着它也可以被克隆;

(4)TreeSet实现了Serializable接口,可以被序列化,可以使用hessian协议来传输;

具有如下特点:

  • 对插入的元素进行排序,是一个有序的集合(主要与HashSet的区别);
  • 底层使用红黑树结构,而不是哈希表结构;
  • 允许插入Null值;
  • 不允许插入重复元素;
  • 线程不安全;

TreeSet检查重复的原理
TreeSet调用add方法时,会调用到底层TreeMap的put方法,在put方法中会调用到compare(key, key)方法,进行key大小的比较;
在比较的时候,会将传入的key进行类型强转,所以当我们自定义的App类进行比较的时候,自然就会抛出异常,因为App类并没有实现Comparable接口;

Queue

队列是数据结构中比较重要的一种类型,它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列),跟我们生活中的排队类似。
队列有两种:

  • 单队列
  • 循环队列




    单队列就是常见的队列, 每次添加元素时,都是添加到队尾
    以数组实现的队列为例,初始时队列长度固定为 4,font 和 rear 均为 0:
    在这里插入图片描述

每添加一个元素,rear 后移一位。当添加四个元素后, rear 到了索引为 4 的位置:
在这里插入图片描述

这时 a1,a2 出队,front 后移动到 2:
在这里插入图片描述

这时想要再添加两个元素,但 rear 后移两位后就会越界:
在这里插入图片描述

明明有三个空位,却只能再放入一个!这就是单队列的“假溢出”情况。
针对这种情况,解决办法就是后面满了,就再从头开始,也就是头尾相接的循环。这就是 “循环队列” 的概念。
循环队列中,

  • rear = (rear - size) % size

接着上面的例子,当 rear 大于 队列长度时,rear = ( 5 - 5) % 5 = 0 :
在这里插入图片描述
这样继续添加时,还可以添加几个元素:
在这里插入图片描述

那如何判断队列是否装满元素了呢,单使用 front == rear 无法判断究竟是空的还是满了。

两种方法:

加个标志 flag ,初始为 false,添加满了置为 true;
不以 front = rear 为放满标志,改为 (rear - front) % size = 1。
法 2 的公式放满元素时空余了一个位置,这个公式是什么意思呢?
在这里插入图片描述
接着上面的情况,当 rear 从后面添加元素跑到前面 0 时,再添加一个元素 a6,rear 后移一位到 1,这时 front = 2, (1 - 2) % 5 = 1, 满足放满条件。

因此,当 rear > font 时,队列中元素个数 = rear - font;
当 rear < font 时,队列中元素分为两部分: size - font 和 rear ,也就是 rear + size - font。以上述图片为例,队列中元素个数 = 1 + 5 - 2 = 4.
在这里插入图片描述
接下来看看Queue
在日常生活中,排队几乎随处可见,上地铁要排队,买火车票要排队,就连出门吃个大餐,也要排队。。。之前研究的ArrayList就像是一个缺乏管理的排队系统。大家虽然会排队,但一会走个人,大家向前挪一挪,一会插个人,大家向后挪一挪,碰到这样的队伍,一定让人痛苦万分吧?

今天要介绍的Queue就不同了,它是一个严格的排队系统。就像许多火车站排队窗口在两侧加了护栏一样,大家只能从队尾进来,从队首离开,我们称之为FIFO(first in first out),也就是先进来的人先离开。Queue就严格遵循了这个原则,使插队和提早离开变得不可能。

当然Queue也有很多变种,FIFO并不是其可以遵循的唯一规则。比如Stack(栈),就遵循LIFO(last in first out),这就好比我们叠碗一样,后来者居上。还有我们之后要分析的Deque,其允许元素从两端插入或删除,比如排队进站时总有人说,“我能不能插个队,我赶时间?”。

首先,Queue也继承自Collection,说明它是集合家族的一员。Queue接口主要提供了以下方法:

//将元素插入队列
boolean add(E e);

//将元素插入队列,与add相比,在容量受限时应该使用这个
boolean offer(E e);

//将队首的元素删除,队列为空则抛出异常
E remove();

//将队首的元素删除,队列为空则返回null
E poll();

//获取队首元素,但不移除,队列为空则抛出异常
E element();

//获取队首元素,但不移除,队列为空则返回null
E peek();

感谢Thanks

  • https://www.jianshu.com/p/b48c47a42916#:~:text=Set%E7%BB%A7%E6%89%BF%E4%BA%8ECollection%E6%8E%A5%E5%8F%A3%EF%BC%8C%E6%98%AF%E4%B8%80%E4%B8%AA%E4%B8%8D%E5%85%81%E8%AE%B8%E5%87%BA%E7%8E%B0%E9%87%8D%E5%A4%8D%E5%85%83%E7%B4%A0%EF%BC%8C%E5%B9%B6%E4%B8%94%E6%97%A0%E5%BA%8F%E7%9A%84%E9%9B%86%E5%90%88%EF%BC%8C%E4%B8%BB%E8%A6%81%E6%9C%89HashSet%E5%92%8CTreeSet%E4%B8%A4%E5%A4%A7%E5%AE%9E%E7%8E%B0%E7%B1%BB%E3%80%82%20%E5%9C%A8%E5%88%A4%E6%96%AD%E9%87%8D%E5%A4%8D%E5%85%83%E7%B4%A0%E7%9A%84%E6%97%B6%E5%80%99%EF%BC%8CSet%E9%9B%86%E5%90%88%E4%BC%9A%E8%B0%83%E7%94%A8hashCode,%28%29%E5%92%8Cequal%20%28%29%E6%96%B9%E6%B3%95%E6%9D%A5%E5%AE%9E%E7%8E%B0%E3%80%82https://home.firefoxchina.cn
  • https://blog.csdn.net/mashaokang1314/article/details/83721792
  • https://blog.csdn.net/u011240877/article/details/52860924
  • https://www.jianshu.com/p/41752e1a0978

有任何不对的地方,欢迎联系改正!!!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值