Java集合框架总结大纲

一、Java集合框架概述

集合可以看作是一种容器,用来存储对象信息。所有集合类都位于java.util包下,但支持多线程的集合类位于java.util.concurrent包下,下面进行详细概述:

  1. 集合的发展分支图
    在这里插入图片描述
  2. 集合的概念
    集合可以说是一种保存对象的容器,他可以将我们需要的多个对象进行存储,来方便我们对多个对象可以进行的一些操作。我们最先接触的Java容器时数组,但是为什么现在又要接触集合?这就要说明二者的区别了。我们在声明一个数组的时候,就需要确定数组的长度和数组所能存放元素的类型,然后才可以进行其他操作,这样数组的局限性也就体现出来了:当我们存储的数据在增多并且超过我们原先声明的长度时,我们需要扩展数组的长度才可以继续往里添加数据;而且数组中提供给我们操作数据的方法比较少;最后就是数组存储的数据只能是有序可以重复的,数据的特点比较单一,无法应对现在我们的复杂需求。所以集合就出现了,他可以很好的解决数组存在的问题。
  3. 数组与集合的差别
    在这里插入图片描述

二、Java集合常见接口及实现类

  1. Collection接口常见方法(来源于Java API)
  • boolean add(E e)

向集合中添加一个元素。集合更改则添加成功返回true,如果该集合不允许重复并且已经包含指定的元素。返回false。部分子类的add方法可能会限制添加到集合中的元素类型,或者不会将NULL添加到集合中。

  • boolean addAll(Collection<? extends E> c)

将指定集合中的所有元素添加到此集合中。在添加过程中如果被添加的集合发生了更改,addAll方法不具有幂等性。

  • void clear()

清空掉集合中的所有元素

  • boolean contains(Object o)

如果集合中包含指定元素那么返回true。特别的,如果集合中也包含NULL元素的时候并且要查找的元素也是NULL的时候也返回true。

  • boolean containsAll(Collection<?> c)

如果该集合中包含指定集合中的所有元素的时候返回true。

  • boolean isEmpty()

如果集合中没有元素返回true。

  • boolean remove(Object o)

删除集合中的指定的元素。如果存在NULL,也删除。

  • boolean removeAll(Collection<?> c)

删除当前集合中所有等于指定集合中的元素。

  • boolean retainAll(Collection<?> c)

仅保留该指定集合中存在的所有元素。其余删除

  • int size()

返回该集合中元素的个数。如果超过了Integer.MAX_VALUE,那么返回Integer.MAX_VALUE。

  • Object[] toArray()
    这个方法是集合和数组转化的桥梁。

见名知意,返回包含此集合中所有元素的数组。如果这个集合的迭代器保证元素有序,那么该方法与其迭代器中元素顺序一致。并且该方法返回的数组是拷贝出来的(某些集合底层数组实现,区别这个),可以进行任意的更改。

  • <T> T[] toArray(T[] a)
    该方法可以对返回的数组类型进行精确控制。而非像toArray方法一样返回Object[]。

返回集合中所有元素到该数组中。如果这个数组可以容纳下的话,否则返回一个新new的数组,容量和集合中元素数量一致。如果指定的数组容量大于集合中元素个数,数组空闲位置填NULL。如果这个集合的Iterator具有顺序性的话,数组元素顺序与该迭代器一致。

  1. Set集合(HashSet类、LinkedHashSet类、TreeSet类、EnumSet类、各Set实现类的性能分析)
  • HashSet类

    hash表可以存储元素位置为桶,通常一个桶只有一个元素,hash算法在计算时出现hash冲突时会在一个桶下装两个以上的元素。
    hashmap与hashset包含如下属性:

      容量:hash桶中的数量。
      初始化容量:创建表时桶的数量。
      尺寸:(size)hash中记录的数量
      负载因子:负载因子等于size/capacity 。负载因子为0表示空表。
    

    hash表中有个负载极限,负载极限是0-1的数值,负载极限决定了hash表的最大填满程度,当hash表中的负载因子达到指定的负载极限时,会成倍的增加桶的容量。将原有对象重新分配。这称为rehashing.

    hashset中默认的负载极限为0.75,负载极限是时间与空间上的一种折中成本,较高的负载极限可以降低hash表所占用的内存空间。会增加查询数据的时间开销,较低的负载极限会提高性能,但是增加hash表的所占用的内存开销。

  • LinkedHashSet类
    具有可预知迭代顺序的Set 接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于,它维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到set中的顺序(插入顺序)进行迭代。注意,插入顺序不受在set中重新插入的元素影响。此实现不是同步的。

  • TreeSet类
    基于TreeMap的NavigableSet实现。使用元素的自然顺序对元素进行排序,或者根据创建set时提供的 Comparator 进行排序,具体取决于使用的构造方法。此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。此实现不是同步的。

  • EnumSet类
    EnumSet只能存放一种枚举类型的元素,具体存放什么枚举类型的元素可以通过两种方法指定,一种是显式,一种是隐式;

    一旦元素的枚举类型确定那么集合就确定了(即只能存放该种枚举类型的元素,不能同时存放多种枚举类型的元素!!否则就会抛出异常);

    枚举集合EnumSet底层并不是直接存放枚举对象的,而是用二进制位向量来存放的,因此存储非常紧凑,并且高效(比如枚举类型A里有4个枚举值a、b、c、d,并且这4个值都是对象,现在一个枚举集合存放a、c、d三个值,由于枚举类型的所有值的个数是有限的,因此可以用二进制序列来唯一表示,这里就用4位二进制数表示,现在只有a、c、d这三个值,因此可以表示为1011,1表示该枚举值在集合中,0表示不在集合种;

  • 各Set实现类的性能分析
    HashSet 和TreeSet是Set的典型实现。HashSet 比TreeSet性能好,TreeSet需要额外通过红黑树算法维护集合 的顺序。除了需要维护集合的顺序外,其他的都优先用HashSet 。

    LinkedHashSet 比HashSet 慢,但是因为有链表,所以遍历他就更快。

    EnumSet是性能最好的,但是只能保存同一个枚举类的成员作为元素。

    注意:该三个实现类都不是线程安全的。需要手动保证Set集合的同步

  1. List集合(ArrayList、LinkedList、Vector、Stack、Iterator接口和ListIterator接口,各List实现类的性能分析)
  • ArrayList
    ArrayList的构造主要从AbstractList实现,主要是判断下初始元素的容量,ArrayList最大的特点就是提供了Add、Get操作,当然可以通过迭代器来遍历,对于元素的存在可以通过contains方法判断。

    底层才有数组结构算法,方法没有使用synchronized修饰,线程不安全。

  • LinkedList
    作为一种双向链表结构,对于元素的插入、删除效率比较高,只需要调整节点指向即可,但是对于随机查找而言性能主要看这个链表长度和运气了。方法也没有使用synchronized修饰,线程不安全.。 LinkedList也提供了ArrayList的get方法,但是要复杂的多,主要通过next或previous方法遍历得到。

  • Vector
    Vector是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。

    Vector的内存机制:
    简单地讲就是一个动态数组,里面有一个指针指向一片连续的内存空间,当空间不够装下数据时会自动申请另一片更大的空间,然后把原有数据拷贝过去,接着释放原来的那片空间;当释放或者说是删除里面的数据时,其存储空间并不会释放,仅仅只是清空了里面的数据。

  • 各List实现类的性能分析
    Vector类: 底层才有数组结构算法,方法都使用了synchronized修饰,线程安全,但是性能相对于ArrayList较低.

    ArrayList类: 底层才有数组结构算法,方法没有使用synchronized修饰,线程不安全,性能相对于Vector较高.
    ArrayList现在机会已经取代了Vector的江湖地位.
    为了保证ArrayList的线程安全,List list = Collections.synchronizedList(new ArrayList(…));

    LinkedList类:底层才有双向链表结构算法,方法没有使用synchronized修饰,线程不安全.

    数组结构算法和双向链表结构算法的性能问题:
    数组结构算法: 插入和删除操作速度低,查询和更改较快.

  1. Map集合(HashMap、Hashtable、LinkedHashMap实现类、TreeMap实现类、Map实现类的性能分析)
  • HashMap
    HashMap是Java中大家最常用的一个map实现类,其为键值对也就是key-value的形式。他的数据结构则是采用的位桶和链表相结合的形式完成了,即拉链法。具体如下图所示:
    在这里插入图片描述
    HashMap是线程不安全的,性能较高,可以使用null作为key或者value。

  • Hashtable
    PHP中数组的本质是顺序字典,它可以表示一个包含键值对的顺序列表,键值对的映射就是使用Hashtable实现的。

    Hashtable是非常常见的数据结构,它被设计出来解决计算机只能直接表示以连续的整数作为索引的数组的问题。使用Hashtable,程序员才能使用字符串或者其他的复合类型作为数组的键。

    Hashtable的概念实际上非常简单:字符串的键先会被传递给一个hash函数(hashing function,中文也翻译为散列函数,本文统一使用hash函数),然后这个函数会返回一个整数(我们把它叫做hash值),而这个整数就是“通常”的数组的索引。问题是对于两个不同的字符串,调用hash函数会得到同一个hash值,而现实情况是任意字符串都可以作为键,所以键会有无数个,而数组的大小必须是提前设定好的,因为hash值必须小于数组索引的最大值,所以可以生成的hash值必须是有限的。这样用有限的hash值表示无限的键,必然会导致冲突。我们把两个不同的键的hash值是一样的情况称为冲突,任何Hashtable算法都必须提供某种机制解决这种冲突。

    有两种主要的处理冲突的方法。开放定址法,当冲突发生的时候,冲突的元素会被保存到一个不同的索引中;链接法,所有拥有相同的hash值的元素,它们都会被保存到一个链表中。PHP使用的就是第二种方法。

    另外通常情况下,Hashtable并非是显式排序的:最终底层数组中保存的元素的顺序是跟hash函数相关的,并且这个顺序一般都是随机的。这个行为显然跟PHP数组的语义不符:PHP的数组的迭代顺序跟数组中元素的插入顺序完全一致。这也意味着,PHP中的Hashtable的实现必须有一种额外的机制记住数组中元素的插入顺序。

  • LinkedHashMap
    HashMap和双向链表合二为一即是LinkedHashMap。所谓LinkedHashMap,其落脚点在HashMap,因此更准确地说,它是一个将所有Entry节点链入一个双向链表的HashMap。

    由于LinkedHashMap是HashMap的子类,所以LinkedHashMap自然会拥有HashMap的所有特性。比如,LinkedHashMap的元素存取过程基本与HashMap基本类似,只是在细节实现上稍有不同。当然,这是由LinkedHashMap本身的特性所决定的,因为它额外维护了一个双向链表用于保持迭代顺序。

  • TreeMap
    TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;
    继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现;
    实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;
    TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序

  • 性能分析
    对于Map的常用实现类而言,虽然HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的、线程安全的集合,所以HashMap通常比Hashtable要快。

    TreeMap通常比HashMap、Hashtable要慢(尤其在插入、删除key-value时更慢),因为TreeMap底层采用红黑树来管理key-value对(红黑树的每一个节点就是一个key-value键值对)。

    使用TreeMap有一个好处:TreeMap中的key-value总是处于有序状态,无须专门进行排序操作,当TreeMap被填充之后,就可以调用keySet(),取得由key组成的Set,然后使用toArray()方法生成key的数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速查找对象。

    对于一般的应用场景,程序应该多考虑使用HashMap,因为HashMap正是为了快速查询而设计的(HashMap底层其实也是 采用数组来存储key-value对),但如果程序需要一个总是排好序的Map,则可以考虑使用TreeMap。

    LinkedHashMap比HashMap慢一点,因为它需要维护链表来保持Map中key-value添加时的顺序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值