JAVA常用数据结构及原理分析  http://www.2cto.com/kf/201506/412305.html

前不久面试官让我说一下怎么理解java数据结构框架,之前也看过部分
源码
,balabala讲了一堆,现在总结一下。

java.util包中三个重要的接口及特点:
List(列表)、Set(保证集合中元素唯一)、Map(维护多个key-value键值对,保证key唯一)。

其不同子类的实现各有差异,如是否同步(线程安全)、是否有序。
常用类继承树:
这里写图片描述

以下结合源码讲解常用类实现原理及相互之间的差异。

Collection (所有集合类的接口)
List、Set都继承自Collection接口,查看JDK
API,操作集合常用的方法大部分在该接口中定义了。
这里写图片描述

Collections
(操作集合的工具类)
对于集合类的操作不得不提到工具类
Collections
,它提供了许多方便的方法,如求两个集合的差集、并集、拷贝、排序等等。
由于大部分的集合接口实现类都是不同步的,可以使用Collections.synchronized*方法创建同步的集合类对象。
如创建一个同步的List:
List synList = Collections.synchronizedList(new ArrayList());
其实现原理就是重新封装new出来的对象,操作对象时用关键字synchronized同步。看源码很容易理解。
Collections部分源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
<code
class
=
"language-java hljs "
>
//Collections.synchronizedList返回的是静态类SynchronizedCollection的实例,最终将new出来的ArrayList对象赋值给了Collection<e> c。
static
class
SynchronizedCollection<e>
implements
Collection<e>, Serializable {

final
Collection<e> c;
// Backing Collection

final
Object mutex;
// Object on which to synchronize

SynchronizedCollection(Collection<e> c) {

if
(c==
null
)

throw
new
NullPointerException();

this
.c = c;

mutex =
this
;

}

//...

public
boolean
add(E e) {

//操作集合时简单调用原本的ArrayList对象,只是做了同步

synchronized
(mutex) {
return
c.add(e);}

}

//...
}</e></e></e></e></e></code>

List (列表)

ArrayList、Vector是线性表,使用Object数组作为容器去存储数据的,添加了很多方法维护这个数组,使其容量可以动态增长,极大地提升了开发效率。它们明显的区别是ArrayList是非同步的,Vector是同步的。不用考虑多线程时应使用ArrayList来提升效率。
ArrayList、Vector
部分源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31
<code
class
=
"language-java hljs "
>
//ArrayList.add
public
boolean
add(E e) {

ensureCapacityInternal(size +
1
);
// Increments modCount!!

//可以看出添加的对象放到elementData数组中去了

elementData[size++] = e;

return
true
;
}
//ArrayList.remove
public
E remove(
int
index) {

rangeCheck(index);

modCount++;

E oldValue = elementData(index);

int
numMoved = size - index -
1
;

if
(numMoved >
0
)

//移除元素时数组产生的空位由System.arraycopy方法将其后的所有元素往前移一位,System.arraycopy调用虚拟机提供的本地方法来提升效率

System.arraycopy(elementData, index+
1
, elementData, index,

numMoved);

elementData[--size] =
null
;
// Let gc do its work

return
oldValue;
}
//Vector add方法上多了synchronized关键字
public
synchronized
boolean
add(E e) {

modCount++;

ensureCapacityHelper(elementCount +
1
);

elementData[elementCount++] = e;

return
true
;
}</code>

LinkedList是链表,略懂数据结构就知道其实现原理了。链表随机位置插入、删除数据时比线性表快,遍历比线性表慢。
双向链表原理图:
这里写图片描述
LinkedList部分源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21
<code
class
=
"language-java hljs "
>
//源码很清晰地表达了原理图
public
class
LinkedList<e>
extends
AbstractSequentialList<e>
implements
List<e>, Deque<e>, Cloneable, java.io.Serializable
{

//头尾节点

transient
Node<e> first;

transient
Node<e> last;
}
//节点类

private
static
class
Node<e> {

//节点存储的数据

E item;

Node<e> next;

Node<e> prev;

Node(Node<e> prev, E element, Node<e> next) {

this
.item = element;

this
.next = next;

this
.prev = prev;

}
}</e></e></e></e></e></e></e></e></e></e></e></code>

由此可根据实际情况来选择使用ArrayList(非同步、非频繁删除时选择)、Vector(需同步时选择)、LinkedList(频繁在任意位置插入、删除时选择)。

Map(存储键值对,key唯一)

HashMap结构的实现原理是将put进来的key-value封装成一个Entry对象存储到一个Entry数组中,位置(数组下标)由key的哈希值与数组长度计算而来。如果数组当前下标已有值,则将数组当前下标的值指向新添加的Entry对象。
有点晕,看图吧:
这里写图片描述
看完图再看源码,非常清晰,都不需要注释。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30
<code
class
=
"language-java hljs "
>
public
class
HashMap<k,v>
extends
AbstractMap<k,v>
implements
Map<k,v>, Cloneable, Serializable
{

transient
Entry<k,v>[] table;

public
V put(K key, V value) {

if
(key ==
null
)

return
putForNullKey(value);

int
hash = hash(key);

int
i = indexFor(hash, table.length);

//遍历当前下标的Entry对象链,如果key已存在则替换

for
(Entry<k,v> e = table[i]; e !=
null
; e = e.next) {

Object k;

if
(e.hash == hash && ((k = e.key) == key || key.equals(k))) {

V oldValue = e.value;

e.value = value;

e.recordAccess(
this
);

return
oldValue;

}

}

addEntry(hash, key, value, i);

return
null
;

}
}
static
class
Entry<k,v>
implements
Map.Entry<k,v> {

final
K key;

V value;

Entry<k,v> next;

int
hash;
}</k,v></k,v></k,v></k,v></k,v></k,v></k,v></k,v></code>

TreeMap是由Entry对象为节点组成的一颗
红黑树
,put到TreeMap的数据默认按key的自然顺序排序,new
TreeMap时传入Comparator自定义排序。红黑树网上很多资料,我讲不清,这里就不介绍了。

Set(保证容器内元素唯一性)
之所以先讲Map是因为
Set结构其实就是维护一个Map来存储数据的,利用Map结构key值唯一性

HashSet部分源码:

1

2

3

4

5

6

7

8

9

10

11
<code
class
=
"language-java hljs "
>
public
class
HashSet<e>
extends
AbstractSet<e>
implements
Set<e>, Cloneable, java.io.Serializable
{

//无意义对象来作为Map的value

private
static
final
Object PRESENT =
new
Object();

public
boolean
add(E e) {

return
map.put(e, PRESENT)==
null
;

}
}</e></e></e></code>

HashSet、TreeSet分别默认维护一个HashMap、TreeMap。

标签: none

添加新评论