线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
/*****************************************************************************************************************/
LinkedeList和ArrayList都实现了List接口,但是它们的工作原理却不一样。它们之间最主要的区别在于ArrayList是可改变大小的数组,而LinkedList是双向链接串列(doubly LinkedList)。ArrayList更受欢迎,很多场景下ArrayList比LinkedList更为适用。这篇文章中我们将会看看LinkedeList和ArrayList的不同,而且我们试图来看看什么场景下更适宜使用LinkedList,而不用ArrayList。
LinkedList和ArrayList的区别
LinkedList和ArrayList的差别主要来自于Array和LinkedList数据结构的不同。如果你很熟悉Array和LinkedList,你很容易得出下面的结论:
1) 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
2) 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
3)类似于插入数据,删除数据时,LinkedList也优于ArrayList。
4) LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
什么场景下更适宜使用LinkedList,而不用ArrayList
我前面已经提到,很多场景下ArrayList更受欢迎,但是还有些情况下LinkedList更为合适。譬如:
1) 你的应用不会随机访问数据。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据。
2) 你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。
以上就是关于ArrayList和LinkedList的差别。你需要一个不同步的基于索引的数据访问时,请尽量使用ArrayList。ArrayList很快,也很容易使用。但是要记得要给定一个合适的初始大小,尽可能的减少更改数组的大小。
1. List的五种遍历方式
下面只是简单介绍各种遍历示例(以ArrayList为例),各自优劣会在本文后面进行分析给出结论。
(1) for each循环
1
2
3
4
|
List
<Integer>
list
=
new
ArrayList
<Integer>
(
)
;
for
(
Integer
j
:
list
)
{
// use j
}
|
(2) 显示调用集合迭代器
1
2
3
4
|
List
<Integer>
list
=
new
ArrayList
<Integer>
(
)
;
for
(
Iterator
<Integer>
iterator
=
list
.
iterator
(
)
;
iterator
.
hasNext
(
)
;
)
{
iterator
.
next
(
)
;
}
|
或
1
2
3
4
5
|
List
<Integer>
list
=
new
ArrayList
<Integer>
(
)
;
Iterator
<Integer>
iterator
=
list
.
iterator
(
)
;
while
(
iterator
.
hasNext
(
)
)
{
iterator
.
next
(
)
;
}
|
(3) 下标递增循环,终止条件为每次调用size()函数比较判断
1
2
3
4
|
List
<Integer>
list
=
new
ArrayList
<Integer>
(
)
;
for
(
int
j
=
0
;
j
<
list
.
size
(
)
;
j
++
)
{
list
.
get
(
j
)
;
}
|
(4) 下标递增循环,终止条件为和等于size()的临时变量比较判断
1
2
3
4
5
|
List
<Integer>
list
=
new
ArrayList
<Integer>
(
)
;
int
size
=
list
.
size
(
)
;
for
(
int
j
=
0
;
j
<
size
;
j
++
)
{
list
.
get
(
j
)
;
}
|
(5) 下标递减循环
1
2
3
4
|
List
<Integer>
list
=
new
ArrayList
<Integer>
(
)
;
for
(
int
j
=
list
.
size
(
)
-
1
;
j
>=
0
;
j
--
)
{
list
.
get
(
j
)
;
}
|
在测试前大家可以根据对ArrayList和LinkedList数据结构及Iterator的了解,想想上面五种遍历方式哪个性能更优。
2、List五种遍历方式的性能测试及对比
以下是性能测试代码,会输出不同数量级大小的ArrayList和LinkedList各种遍历方式所花费的时间。
PS:如果运行报异常in thread “main” java.lang.OutOfMemoryError: Java heap space,请将main函数里面list size的大小减小。
其中getArrayLists函数会返回不同size的ArrayList,getLinkedLists函数会返回不同size的LinkedList。
loopListCompare函数会分别用上面的遍历方式1-5去遍历每一个list数组(包含不同大小list)中的list。
print开头函数为输出辅助函数。
测试环境为Windows7 32位系统 3.2G双核CPU 4G内存,Java 7,Eclipse -Xms512m -Xmx512m
最终测试结果如下:
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
|
compare
loop
performance
of
ArrayList
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
list
size
|
10
,
000
|
100
,
000
|
1
,
000
,
000
|
10
,
000
,
000
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
each
|
1
ms
|
3
ms
|
14
ms
|
152
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
iterator
|
0
ms
|
1
ms
|
12
ms
|
114
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
list
.
size
(
)
|
1
ms
|
1
ms
|
13
ms
|
128
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
size
=
list
.
size
(
)
|
0
ms
|
0
ms
|
6
ms
|
62
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
j
--
|
0
ms
|
1
ms
|
6
ms
|
63
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
compare
loop
performance
of
LinkedList
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
list
size
|
100
|
1
,
000
|
10
,
000
|
100
,
000
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
each
|
0
ms
|
1
ms
|
1
ms
|
2
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
iterator
|
0
ms
|
0
ms
|
0
ms
|
2
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
list
.
size
(
)
|
0
ms
|
1
ms
|
73
ms
|
7972
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
size
=
list
.
size
(
)
|
0
ms
|
0
ms
|
67
ms
|
8216
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
j
--
|
0
ms
|
1
ms
|
67
ms
|
8277
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
|
第一张表为ArrayList对比结果,第二张表为LinkedList对比结果。
表横向为同一遍历方式不同大小list遍历的时间消耗,纵向为同一list不同遍历方式遍历的时间消耗。
PS:由于首次遍历List会稍微多耗时一点,for each的结果稍微有点偏差,将测试代码中的几个Type顺序调换会发现,for each耗时和for iterator接近。
3、遍历方式性能测试结果分析
(1) foreach介绍
foreach是Java SE5.0引入的功能很强的循环结构,for (Integer j : list)应读作for each int in list。
for (Integer j : list)实现几乎等价于
1
2
3
4
|
Iterator
<Integer>
iterator
=
list
.
iterator
(
)
;
while
(
iterator
.
hasNext
(
)
)
{
Integer
j
=
iterator
.
next
(
)
;
}
|
下面的分析会将foreach和显示调用集合迭代器两种遍历方式归类为Iterator方式,其他三种称为get方式遍历。
这时我们已经发现foreach的一大好处,简单一行实现了四行的功能,使得代码简洁美观,另一大好处是相对于下标循环而言的,foreach不必关心下标初始值和终止值及越界等,所以不易出错。Effective-Java中推荐使用此种写法遍历,本文会验证这个说法。
使用foreach结构的类对象必须实现了Iterable接口,Java的Collection继承自此接口,List实现了Collection,这个接口仅包含一个函数,源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package
java
.
lang
;
import
java
.
util
.
Iterator
;
/**
* Implementing this interface allows an object to be the target of
* the "foreach" statement.
*
* @param <T> the type of elements returned by the iterator
*
* @since 1.5
*/
public
interface
Iterable
<T>
{
/**
* Returns an iterator over a set of elements of type T.
*
* @return an Iterator.
*/
Iterator
<T>
iterator
(
)
;
}
|
iterator()用于返回一个Iterator,从foreach的等价实现中我们可以看到,会调用这个函数得到Iterator,再通过Iterator的next()得到下一个元素,hasNext()判断是否还有更多元素。Iterator源码如下:
1
2
3
4
5
6
7
|
public
interface
Iterator
<E>
{
boolean
hasNext
(
)
;
E
next
(
)
;
void
remove
(
)
;
}
|
(2) ArrayList遍历方式结果分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
compare
loop
performance
of
ArrayList
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
list
size
|
10
,
000
|
100
,
000
|
1
,
000
,
000
|
10
,
000
,
000
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
each
|
1
ms
|
3
ms
|
14
ms
|
152
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
iterator
|
0
ms
|
1
ms
|
12
ms
|
114
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
list
.
size
(
)
|
1
ms
|
1
ms
|
13
ms
|
128
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
size
=
list
.
size
(
)
|
0
ms
|
0
ms
|
6
ms
|
62
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
j
--
|
0
ms
|
1
ms
|
6
ms
|
63
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
|
PS:由于首次遍历List会稍微多耗时一点,for each的结果稍微有点偏差,将测试代码中的几个Type顺序调换会发现,for each耗时和for iterator接近。
从上面我们可以看出:
a. 在ArrayList大小为十万之前,五种遍历方式时间消耗几乎一样
b. 在十万以后,第四、五种遍历方式快于前三种,get方式优于Iterator方式,并且
1
2
3
4
|
int
size
=
list
.
size
(
)
;
for
(
int
j
=
0
;
j
<
size
;
j
++
)
{
list
.
get
(
j
)
;
}
|
用临时变量size取代list.size()性能更优。我们看看ArrayList中迭代器Iterator和get方法的实现
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
|
private
class
Itr
implements
Iterator
<E>
{
int
cursor
;
// index of next element to return
int
lastRet
=
-
1
;
// index of last element returned; -1 if no such
int
expectedModCount
=
modCount
;
public
boolean
hasNext
(
)
{
return
cursor
!=
size
;
}
@SuppressWarnings
(
"unchecked"
)
public
E
next
(
)
{
checkForComodification
(
)
;
int
i
=
cursor
;
if
(
i
>=
size
)
throw
new
NoSuchElementException
(
)
;
Object
[
]
elementData
=
ArrayList
.
this
.
elementData
;
if
(
i
>=
elementData
.
length
)
throw
new
ConcurrentModificationException
(
)
;
cursor
=
i
+
1
;
return
(
E
)
elementData
[
lastRet
=
i
]
;
}
……
}
public
E
get
(
int
index
)
{
rangeCheck
(
index
)
;
return
elementData
(
index
)
;
}
|
从中可以看出get和Iterator的next函数同样通过直接定位数据获取元素,只是多了几个判断而已。
c . 从上可以看出即便在千万大小的ArrayList中,几种遍历方式相差也不过50ms左右,且在常用的十万左右时间几乎相等,考虑foreach的优点,我们大可选用foreach这种简便方式进行遍历。
(3) LinkedList遍历方式结果分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
compare
loop
performance
of
LinkedList
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
list
size
|
100
|
1
,
000
|
10
,
000
|
100
,
000
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
each
|
0
ms
|
1
ms
|
1
ms
|
2
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
iterator
|
0
ms
|
0
ms
|
0
ms
|
2
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
list
.
size
(
)
|
0
ms
|
1
ms
|
73
ms
|
7972
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
size
=
list
.
size
(
)
|
0
ms
|
0
ms
|
67
ms
|
8216
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
j
--
|
0
ms
|
1
ms
|
67
ms
|
8277
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
|
PS:由于首次遍历List会稍微多耗时一点,for each的结果稍微有点偏差,将测试代码中的几个Type顺序调换会发现,for each耗时和for iterator接近。
从上面我们可以看出:
a 在LinkedList大小接近一万时,get方式和Iterator方式就已经差了差不多两个数量级,十万时Iterator方式性能已经远胜于get方式。
我们看看LinkedList中迭代器和get方法的实现
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
private
class
ListItr
implements
ListIterator
<E>
{
private
Node
<E>
lastReturned
=
null
;
private
Node
<E>
next
;
private
int
nextIndex
;
private
int
expectedModCount
=
modCount
;
ListItr
(
int
index
)
{
// assert isPositionIndex(index);
next
=
(
index
==
size
)
?
null
:
node
(
index
)
;
nextIndex
=
index
;
}
public
boolean
hasNext
(
)
{
return
nextIndex
<
size
;
}
public
E
next
(
)
{
checkForComodification
(
)
;
if
(
!
hasNext
(
)
)
throw
new
NoSuchElementException
(
)
;
lastReturned
=
next
;
next
=
next
.
next
;
nextIndex
++
;
return
lastReturned
.
item
;
}
……
}
public
E
get
(
int
index
)
{
checkElementIndex
(
index
)
;
return
node
(
index
)
.
item
;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node
<E>
node
(
int
index
)
{
// assert isElementIndex(index);
if
(
index
<
(
size
>>
1
)
)
{
Node
<E>
x
=
first
;
for
(
int
i
=
0
;
i
<
index
;
i
++
)
x
=
x
.
next
;
return
x
;
}
else
{
Node
<E>
x
=
last
;
for
(
int
i
=
size
-
1
;
i
>
index
;
i
--
)
x
=
x
.
prev
;
return
x
;
}
}
|
从上面代码中可以看出LinkedList迭代器的next函数只是通过next指针快速得到下一个元素并返回。而get方法会从头遍历直到index下标,查找一个元素时间复杂度为哦O(n),遍历的时间复杂度就达到了O(n2)。
所以对于LinkedList的遍历推荐使用foreach,避免使用get方式遍历。
(4) ArrayList和LinkedList遍历方式结果对比分析
从上面的数量级来看,同样是foreach循环遍历,ArrayList和LinkedList时间差不多,可将本例稍作修改加大list size会发现两者基本在一个数量级上。
但ArrayList get函数直接定位获取的方式时间复杂度为O(1),而LinkedList的get函数时间复杂度为O(n)。
再结合考虑空间消耗的话,建议首选ArrayList。对于个别插入删除非常多的可以使用LinkedList。
4、结论总结
通过上面的分析我们基本可以总结下:
(1) 无论ArrayList还是LinkedList,遍历建议使用foreach,尤其是数据量较大时LinkedList避免使用get遍历。
(2) List使用首选ArrayList。对于个别插入删除非常多的可以使用LinkedList。
(3) 可能在遍历List循环内部需要使用到下标,这时综合考虑下是使用foreach和自增count还是get方式。
HashMap的测试/
1. Map的四种遍历方式
下面只是简单介绍各种遍历示例(以HashMap为例),各自优劣会在本文后面进行分析给出结论。
(1) for each map.entrySet()
1
2
3
4
5
|
Map
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>
(
)
;
for
(
Entry
<
String
,
String
>
entry
:
map
.
entrySet
(
)
)
{
entry
.
getKey
(
)
;
entry
.
getValue
(
)
;
}
|
(2) 显示调用map.entrySet()的集合迭代器
1
2
3
4
5
6
|
Iterator
<
Map
.
Entry
<
String
,
String
>>
iterator
=
map
.
entrySet
(
)
.
iterator
(
)
;
while
(
iterator
.
hasNext
(
)
)
{
Map
.
Entry
<
String
,
String
>
entry
=
iterator
.
next
(
)
;
entry
.
getKey
(
)
;
entry
.
getValue
(
)
;
}
|
(3) for each map.keySet(),再调用get获取
1
2
3
4
|
Map
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>
(
)
;
for
(
String
key
:
map
.
keySet
(
)
)
{
map
.
get
(
key
)
;
}
|
(4) for each map.entrySet(),用临时变量保存map.entrySet()
1
2
3
4
5
|
Set
<
Entry
<
String
,
String
>>
entrySet
=
map
.
entrySet
(
)
;
for
(
Entry
<
String
,
String
>
entry
:
entrySet
)
{
entry
.
getKey
(
)
;
entry
.
getValue
(
)
;
}
|
在测试前大家可以根据对HashMap的了解,想想上面四种遍历方式哪个性能更优。
2、HashMap四种遍历方式的性能测试及对比
以下是性能测试代码,会输出不同数量级大小的HashMap各种遍历方式所花费的时间。
PS:如果运行报异常in thread “main” java.lang.OutOfMemoryError: Java heap space,请将main函数里面map size的大小减小。
其中getHashMaps函数会返回不同size的HashMap。
loopMapCompare函数会分别用上面的遍历方式1-4去遍历每一个map数组(包含不同大小HashMap)中的HashMap。
print开头函数为输出辅助函数,可忽略。
测试环境为Windows7 32位系统 3.2G双核CPU 4G内存,Java 7,Eclipse -Xms512m -Xmx512m
最终测试结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
compare
loop
performance
of
HashMap
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
map
size
|
10
,
000
|
100
,
000
|
1
,
000
,
000
|
2
,
000
,
000
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
each
entrySet
|
2
ms
|
6
ms
|
36
ms
|
91
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
iterator
entrySet
|
0
ms
|
4
ms
|
35
ms
|
89
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
each
keySet
|
1
ms
|
6
ms
|
48
ms
|
126
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
for
entrySet
=
entrySet
(
)
|
1
ms
|
4
ms
|
35
ms
|
92
ms
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
|
表横向为同一遍历方式不同大小HashMap遍历的时间消耗,纵向为同一HashMap不同遍历方式遍历的时间消耗。
PS:由于首次遍历HashMap会稍微多耗时一点,for each的结果稍微有点偏差,将测试代码中的几个Type顺序调换会发现,for each entrySet耗时和for iterator entrySet接近。
3、遍历方式性能测试结果分析
(1) foreach介绍
见:ArrayList和LinkedList的几种循环遍历方式及性能对比分析中介绍。
(2) HashMap遍历方式结果分析
从上面知道for each与显示调用Iterator等价,上表的结果中可以看出除了第三种方式(for each map.keySet()),再调用get获取方式外,其他三种方式性能相当。本例还是hash值散列较好的情况,若散列算法较差,第三种方式会更加耗时。
我们看看HashMap entrySet和keySet的源码
1
2
3
4
5
6
7
8
9
10
11
|
private
final
class
KeyIterator
extends
HashIterator
<K>
{
public
K
next
(
)
{
return
nextEntry
(
)
.
getKey
(
)
;
}
}
private
final
class
EntryIterator
extends
HashIterator
<
Map
.
Entry
<
K
,
V
>>
{
public
Map
.
Entry
<
K
,
V
>
next
(
)
{
return
nextEntry
(
)
;
}
}
|
分别是keySet()和entrySet()返回的set的迭代器,从中我们可以看到只是返回值不同而已,父类相同,所以性能相差不多。只是第三种方式多了一步根据key get得到value的操作而已。get的时间复杂度根据hash算法而异,源码如下:
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
|
public
V
get
(
Object
key
)
{
if
(
key
==
null
)
return
getForNullKey
(
)
;
Entry
<
K
,
V
>
entry
=
getEntry
(
key
)
;
return
null
==
entry
?
null
:
entry
.
getValue
(
)
;
}
/**
* Returns the entry associated with the specified key in the
* HashMap. Returns null if the HashMap contains no mapping
* for the key.
*/
final
Entry
<
K
,
V
>
getEntry
(
Object
key
)
{
int
hash
=
(
key
==
null
)
?
0
:
hash
(
key
)
;
for
(
Entry
<
K
,
V
>
e
=
table
[
indexFor
(
hash
,
table
.
length
)
]
;
e
!=
null
;
e
=
e
.
next
)
{
Object
k
;
if
(
e
.
hash
==
hash
&&
(
(
k
=
e
.
key
)
==
key
||
(
key
!=
null
&&
key
.
equals
(
k
)
)
)
)
return
e
;
}
return
null
;
}
|
get的时间复杂度取决于for循环循环次数,即hash算法。
4、结论总结
从上面的分析来看:
a. HashMap的循环,如果既需要key也需要value,直接用
1
2
3
4
5
|
Map
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>
(
)
;
for
(
Entry
<
String
,
String
>
entry
:
map
.
entrySet
(
)
)
{
entry
.
getKey
(
)
;
entry
.
getValue
(
)
;
}
|
即可,foreach简洁易懂。
b. 如果只是遍历key而无需value的话,可以直接用
1
2
3
4
|
Map
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>
(
)
;
for
(
String
key
:
map
.
keySet
(
)
)
{
// key process
}
|
参考网址:http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/
http://www.importnew.com/6629.html