集合

集合的表示方式

Java集合的实现是采用接口的方式。不同的具体实现类实际上是实现了多个接口。因此我们在选用集合结构时,应该从接口的角度考虑。比如要进行快速存取,就应该考虑实现了RandomAccess随机访问接口的实现类,比如ArrayList。如果想采用队列的数据结构,就应该考虑实现了Queue接口的实现类,如ArrayDeque或LinkedList。
Java在定义好接口后,并没有直接就通过实现具体接口来产生实际的类,而是先构建了几个抽象集合类,再通过对抽象集合类的继承来设计具体的集合类。这么做的好处是当我们新设计一个集合类时,不用去一次覆盖接口的每个方法,对于不常用方法可以直接从抽象类中继承过来。
先贴一张关系图:转自Java集合框架–昨夜星辰
这里写图片描述
集合的接口可以抽象出两个父类接口,Collection接口和Map接口。以及一个用以遍历集合的Iterator接口。并且,由于各种集合的基础是由数组和链表两种方式构成,因此引入一个RandomAccess接口来标识该集合的适用方式(有时候可以看成该集合的基础为数组)。

Collection接口

Collection接口,作为集合的父类接口,他内部主要的方法都是对集合具有普遍性的。
举个比较代表性的方法:Iterator<E> iterator();该方法会用于产生一个迭代器类,该类实现了Iterator接口,主要是用于对集合的遍历。
注意:实现了Iterator<E> iterator();方法也意味着Collection接口实现了Iterable接口。

Iterator接口

迭代器的出现使得我们可以将遍历集合的操作抽象出来,而不用针对不同的集合编写单一的遍历方式,现在我们只用编写迭代器类就可以了。迭代器类主要由4个方法构成,分别是:

  • hasNext()
  • next()
  • remove()
  • checkForComodification()

值得说明的是,迭代器的指针可以理解为指向两个元素之间,每次使用next()都会返回迭代器越过的元素。并且,每调用一次remove()之前都要调用一次next(),否则会出错。
不同的集合还扩展出了不同的迭代器,会在具体集合类中介绍。

Map接口

Map接口指的是映射形式,即含有Key和Value,Key作为索引,Value作为索引所对应的值。他的添加和获取方法名称同Collection不同,分别为V get​(Object key)V put​(K key, V value),具体的类后面再展开。

具体接口

还是这张图
这里写图片描述
从上图可以看出,集合分为两大类,Collection和Map。其实还有Iterator和RandomAccess接口,在这里先不展开。

List接口

作为一个有序集合接口,其主要特点是允许数据的重复性,支持更改顺序,并且支持随机访问(性能依据具体实现类),具体的实现可以由数组或者链表去实现,因此产生了不同的具体类。
该接口值得注意的一个地方,是他有一个 产生ListIterator接口的迭代器方法。 ListIterator接口的特点是可以实现正向和逆向遍历操作。逆向操作方法为E previous()boolean hasPrevious()

Set接口

该接口有两个特点,一个是不允许含有重复元素,另一个是不能使用Set<Set<Integer>>这样的包含结构(这是由不重复性确定的)。

Queue接口

队列接口的特点是先进先出FIFO或者后进先出LIFO,也就是队列和栈的功能。同List一样,依据具体的实现基础,可分为数组实现的ArrayDeque类和链表实现的LinkedList类。

具体集合类分析

ArrayList

参考:
Java 容器源码分析之 ArrayList–JR’s Blog
ArrayList继承了AbstractList抽象类,并实现了List,RandomAccess,Cloneable以及Serializable接口。ArrayList的内部储存采用了数组的形式,因此才有RandomAccess的功能。
要注意的地方有以下几点:

  • ArrayList初始默认容量是10,扩容时,采用的是当前容量的1.5倍容量,即原来容量加0.5倍。而考虑到数组扩容会产生大量的数据移动,因此对于较大的数据录入最好采用一次性分配较多的空间,避免重复扩容。
  • ArrayList会维护一个modCount变量用来记录容器发生结构化修改的次数,这么做的目的,主要是为了进行fast-fail判断。

HashMap

参考:
Java 容器源码分析之 HashMap–JR’s Blog
Java HashMap工作原理及实现–YiKun
Java 8系列之重新认识HashMap–美团点评技术团队
Java HashMap工作原理–ImportNew

内部结构

HashMap使用了一个内部类Entry<K, V>来作为Node存储数据。该内部类主要有四个域,分别是K,V,K的hashcode,以及一个指向下一个Entry<K, V>的指针变量。
HashMap使用Entry<K, V>作为最小的储存单元,然后以数组table(长度始终保持为2的幂次方)为集合的基本结构,并且以该数组table的一个元素为一个桶(bin),对具有相同散列值的Entry<K, V>都放入同一个桶中,桶内采用的是链表(8个Entry<K, V>以下)和红黑树(8个以上)的结构进行保存。具体结构如图:
这里写图片描述
由此可以看出,对于Key值类,如果K是Comparable的(在红黑树内查找时候),就会对HashMap的查询有一定的加速作用。

哈希计算

HashMap的散列化原理有三步:

  • 调用Key的hashCode()得到初步哈希值h;
  • 对h调用自己的hash()方法再次散列化(主要是位操作)得到哈希值hash
  • 将hash值对数组table长度取模,得到该Key在数组中的下标。要注意的是由于数组table长度为2的幂次方,所以取模操作也可以用位操作代替

因此,如果我们能随意改动Key的hashCode()产生的Hash值,那么将在HashMap中产生影响,造成键值丢失。

扩容

HashMap的扩容是指对数组table的长度加倍,也就是将原table的长度乘以2去建立一个新数组,然后把原来的每个Entry再散列化进新数组中。值得注意的是,数组扩容为2的幂次方的一个原因是为了提高再散列化的效率,因为Java的本质储存方式为二进制的。
>

参考:YiKun’s Blog
怎么理解呢?例如我们从16扩展为32时,具体的变化如下所示:
这里写图片描述
因此元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:
这里写图片描述
因此,我们在扩充HashMap的时候,不需要重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”。可以看看下图为16扩充为32的resize示意图:
这里写图片描述
这个设计确实非常的巧妙,既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。

HashMap的视图View–用于遍历

HashMap 提供了三种方式来遍历其中的 Entry、Key 及Value,分别是 Set<Map.Entry<K,V>> entrySet()Set<K> keySet()Collection<V> values()。这三个方法的基本用法返回的都是视图View集合,即一种遍历方式(迭代器),而不是真的返回了一个复制出来的新的Entry数组table。要弄清楚这三个方法的内部实现机制,首先要来看一下内部抽象类 HashIterator
1. HashMap构建了一个迭代器类 HashIterator,利用该类就可以对HashMap的集合进行遍历操作。
2. KeyIteratorValueIteratorEntryIterator 都继承了 HashIterator,区别只在于next() 方法返回的是 Key、Value 还是 Entry。
3. 利用KeyIteratorValueIteratorEntryIterator,可以构成Set<Map.Entry<K,V>> entrySet()Set<K> keySet()Collection<V> values()方法,以返回三种不同的View视图。

线程安全性

HashMap的是线程不安全的,因此可以采用ConcurrentHashMap类。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TimeMagician/article/details/79980786
文章标签: java 集合类
个人分类: JavaSE基础
上一篇Java初始化顺序
下一篇多线程--基础部分
想对作者说点什么? 我来说一句

x-skin集合x-skin集合

2010年03月17日 10.19MB 下载

事件集合js事件集合js事件集合

2010年06月19日 628KB 下载

网络工具集合网络工具集合

2009年03月07日 6.55MB 下载

Hibernate帮助文档集合

2012年10月17日 8.33MB 下载

debugging tools

2017年10月27日 70.68MB 下载

Java集合 练习代码

2017年10月21日 28KB 下载

Java Generics and Collections

2018年05月09日 2.13MB 下载

户型集合户型集合户型集合

2014年09月27日 11.63MB 下载

FLEX资料集合 FLEX资料集合

2010年03月20日 11.26MB 下载

没有更多推荐了,返回首页

关闭
关闭