本文實(shí)例講述了java集合定義與用法。分享給大家供大家參考,具體如下:
java集合大體可分為三類,分別是set、list和map,它們都繼承了基類接口collection,collection接口定義了眾多操作集合的基本方法,如下:
為了訪問(wèn)collection集合,不得不去了解iterator接口。該接口很簡(jiǎn)單,主要用于定義訪問(wèn)集合的方法,如下:
所以上述的三大類子集合必定都繼承了上面2個(gè)接口。其中set集合要求元素不重復(fù),且內(nèi)部無(wú)序,所以訪問(wèn)時(shí)只能根據(jù)元素值來(lái)訪問(wèn);list內(nèi)部為動(dòng)態(tài)數(shù)組,支持有序,元素也可重復(fù),所以往往有index;map所代表的集合是具有key-value的映射關(guān)系的集合,如哈希表。
1. set
1.1 set不可添加相同元素
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
|
import java.util.collection; import java.util.hashset; public class testset { @suppresswarnings ({ "rawtypes" , "unchecked" }) public static void main(string[] args) { collection c1 = new hashset(); person p = new person(); c1.add(p); c1.add(p); system.out.println(c1); collection c2 = new hashset(); string str1 = new string( "123" ); string str2 = new string( "123" ); c2.add(str1); c2.add(str2); system.out.println(c2); } } class person { public person() { } public person(string name) { this .name = name; } public string name; } |
運(yùn)行輸出:
[demo.person@1db9742]
[123]
第一次添加了倆次p對(duì)象,集合不會(huì)重復(fù)添加,所以輸出了[person@1db9742],這很合理。但是第二次明明new了兩個(gè)字符串,str1和str2的引用肯定是不同的,那為什么程序還是會(huì)認(rèn)為是相同的元素呢。查找add(e e)
的源碼,找到了其中的關(guān)鍵部分,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public boolean add(e e) { return map.put(e, present)== null ; } public v put(k key, v value) { if (key == null ) return putfornullkey(value); int hash = hash(key.hashcode()); int i = indexfor(hash, table.length); 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; } } modcount++; addentry(hash, key, value, i); return null ; } |
這一句
1
|
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) |
表明,當(dāng)兩個(gè)對(duì)象的哈希值相等并且對(duì)象的equals方法返回真時(shí),則認(rèn)為兩個(gè)對(duì)象是相同的,并不會(huì)進(jìn)行后面的addentry操作,即不會(huì)添加至集合。
這也就難怪string str1=new string("123")
和string str2=new string("123");
被認(rèn)為是同一個(gè)對(duì)象了,因?yàn)閟tring在做equals的時(shí)候恰好很特殊,只要值相等,則euqals就返回真。
為了測(cè)試源碼是否真的是這么執(zhí)行的,改寫程序如下:
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
|
import java.util.collection; import java.util.hashset; public class testset { @suppresswarnings ({ "rawtypes" , "unchecked" }) public static void main(string[] args) { collection c1 = new hashset(); c1.add( new a()); c1.add( new a()); c1.add( new b()); c1.add( new b()); c1.add( new c()); c1.add( new c()); system.out.println(c1); } } class a { @override public boolean equals(object obj) { return true ; } @override public int hashcode() { return 1 ; } } class b { @override public int hashcode() { return 1 ; } } class c { @override public boolean equals(object obj) { return true ; } } |
輸出:
[demo.a@1, demo.b@1, demo.b@1, demo.c@1db9742, demo.c@106d69c]
可以看到,b和c的對(duì)象都沒有被集合認(rèn)為是同一個(gè)對(duì)象,而a類中重寫的哈希值和equals永遠(yuǎn)相等,導(dǎo)致a類new
出的匿名對(duì)象也是相等的,故只添加了一個(gè)。
1.2 set不可修改元素的值
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
|
import java.util.collection; import java.util.iterator; import java.util.hashset; public class testset { @suppresswarnings ({ "rawtypes" , "unchecked" }) public static void main(string[] args) { collection coll = new hashset(); coll.add( new person( "f" )); coll.add( new person( "l" )); coll.add( new person( "y" )); system.out.println(coll); // a iterator it = coll.iterator(); // b while (it.hasnext()) { person p = (person) it.next(); if (p.name.equals( "f" )) { p = new person(); // c } } iterator it1 = coll.iterator(); // d while (it1.hasnext()) { person p = (person) it1.next(); system.out.println(p.name); } system.out.println(coll); } } class person { public person() { } public person(string name) { this .name = name; } public string name; } |
運(yùn)行輸出:
[demo.person@52e922, demo.person@1db9742, demo.person@106d69c]
y
f
l
[demo.person@52e922, demo.person@1db9742, demo.person@106d69c]
代碼輸出表明,hashset集合的元素并不是有序的,另外在代碼c處取出了元素后,為該元素重新賦值,而后輸出發(fā)現(xiàn)集合并沒有改變,這說(shuō)明iterator迭代器在提供next的方法里應(yīng)該是類似于copy的技術(shù),目的就是防止在遍歷set集合的時(shí)候元素被改變。
2. list
list作為collection的子接口,自然可以調(diào)用父接口的基本方法,但由于list集合元素是有序的,所以list接口在父接口的基礎(chǔ)上又增加了些方法。這些方法的作用與類父接口類似,只是都會(huì)增加一個(gè)index參數(shù)做為索引。
list中最常用的就是arraylist,它在vector的基礎(chǔ)上做了許多改進(jìn),下面代碼將展示list的基本操作用法:
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
|
import java.util.list; import java.util.arraylist; import java.util.listiterator; public class testlist { @suppresswarnings ({ "rawtypes" , "unchecked" }) public static void main(string[] args) { // 向list中添加不同類型的元素,會(huì)自動(dòng)裝箱 list list = new arraylist(); list.add( 1 ); list.add( "123" ); list.add( 3 .14f); // 列表元素:[1, 123, 3.14] system.out.println( "列表元素:" + list); // 清除列表 list.clear(); list.add( "我" ); list.add( "們" ); list.add( "交" ); list.add( "個(gè)" ); list.add( "朋" ); list.add( "友" ); list.add( "吧" ); // 列表元素:[我, 們, 交, 個(gè), 朋, 友, 吧] system.out.println( "列表元素:" + list); list sub1 = list.sublist( 0 , list.size() / 2 ); // 子列表元素:[我, 們, 交] system.out.println( "子列表元素:" + sub1); // 從list中刪除sub sub1.removeall(list); // 列表元素:[個(gè), 朋, 友, 吧] system.out.println( "列表元素:" + list); // 添加至頭 list sub2 = new arraylist(); sub2.add( "我" ); sub2.add( "們" ); sub2.add( "交" ); system.out.println( "子列表元素:" + sub2); // 在list中添加sub2 list.addall( 0 , sub2); system.out.println( "列表元素:" + list); // 遍歷操作 listiterator iter = list.listiterator(); system.out.println( "--正向遍歷--" ); while (iter.hasnext()) { system.out.println(iter.next()); } system.out.println( "--反向遍歷--" ); while (iter.hasprevious()) { system.out.println(iter.previous()); } } } |
運(yùn)行輸出:
列表元素:[1, 123, 3.14]
列表元素:[我, 們, 交, 個(gè), 朋, 友, 吧]
子列表元素:[我, 們, 交]
列表元素:[個(gè), 朋, 友, 吧]
子列表元素:[我, 們, 交]
列表元素:[我, 們, 交, 個(gè), 朋, 友, 吧]
--正向遍歷--
我
們
交
個(gè)
朋
友
吧
--反向遍歷--
吧
友
朋
個(gè)
交
們
我
list就像是一個(gè)動(dòng)態(tài)且元素類型可不一的數(shù)組,它不僅具有iterator迭代器,而且還有l(wèi)istiterator,后者就像數(shù)組一樣,支持正向和反向遍歷。
3. map
map是具有映射關(guān)系的集合,key做為主鍵,可以索引到唯一的value,key和value都可以是對(duì)象。如果單獨(dú)取出map里的所有值的話,map看起來(lái)就像是set,而又由于它較之set又具有索引功能,所以又似乎有些list的影子。實(shí)際上,map的key必須實(shí)現(xiàn)equals和hashcode方法,這也就解釋了為什么可以將一個(gè)對(duì)象的引用做為key了(實(shí)際上是計(jì)算這個(gè)對(duì)象的hashcode做為主鍵),因此不能將同一個(gè)對(duì)象的引用存入某一個(gè)map中。hashset實(shí)現(xiàn)了set接口,arraylist實(shí)現(xiàn)了list接口,那么單從命名上就能得知,hashmap肯定實(shí)現(xiàn)了map接口,map接口的功能如下,
在hashset和arraylist都有一個(gè)訪問(wèn)迭代器的方法iterator()
,在set接口中卻沒有,畢竟set是key-value組合,取而代之的是一個(gè)keyset()
方法,用以返回一個(gè)實(shí)現(xiàn)了set接口的對(duì)象,從而又可以進(jìn)行iterator的操作。
基本操作如下:
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
|
import java.util.hashmap; import java.util.iterator; public class testmap { @suppresswarnings ({ "rawtypes" , "unchecked" }) public static void main(string[] args) { hashmap hash = new hashmap(); hash.put( "1" , "我" ); hash.put( "2" , "們" ); // 主鍵可為null,但只能有一個(gè)null值的主鍵 hash.put( null , null ); // 值可以為null,可以有很多個(gè)值為null hash.put( "3" , null ); hash.put( "4" , null ); system.out.println( "直接遍歷:" + hash); system.out.println( "----keysey遍歷----:" ); for (object key : hash.keyset()) { system.out.println( "key:" + key + " value:" + hash.get(key)); } system.out.println( "----iterator遍歷----:" ); iterator iter = hash.keyset().iterator(); while (iter.hasnext()) { string key = (string) iter.next(); system.out.println( "key:" + key + " value:" + hash.get(key)); } } } |
輸出:
直接遍歷:{null=null, 1=我, 2=們, 3=null, 4=null}
----keysey遍歷----:
key:null value:null
key:1 value:我
key:2 value:們
key:3 value:null
key:4 value:null
----iterator遍歷----:
key:null value:null
key:1 value:我
key:2 value:們
key:3 value:null
key:4 value:null
hashmap可以有空key,但是只能有一個(gè),,這符合唯一主鍵的原則,并且若主鍵重復(fù)了,則會(huì)覆蓋之前的相同主鍵。而值卻沒有限制,有多少個(gè)null都可以。此外,在使用hashmap的時(shí)候還需要注意下面兩點(diǎn):
1.hashmap是非線程安全的,而hashtable是線程安全的。
對(duì)于各種集合的各種操作,其實(shí)可以依賴于collections類,該類提供了許多靜態(tài)操作集合的方法,其中就可以將一個(gè)普通集合封裝為線程安全的集合,如下
1
|
collection c=collections. synchronized ( new arraylist()); |
2.了解hashmap的性能
hashmap利用每一個(gè)key的哈希值,去為value找尋存儲(chǔ)位置。這個(gè)存儲(chǔ)位置往往被稱為“桶”,當(dāng)哈希值唯一時(shí),那么一個(gè)桶中就只有一個(gè)對(duì)象,這時(shí)情況最理想,然而若非正常情況下(比如重寫hashcode強(qiáng)制返回相等),那么一個(gè)桶能就有放多個(gè)對(duì)象,這時(shí)性能最差。
上面說(shuō)道,hashmap與set、list在某方面都很相似,做為一個(gè)強(qiáng)大的集合,它的內(nèi)部自然也有會(huì)動(dòng)態(tài)開辟內(nèi)存的操作。所有就有了下面幾個(gè)參數(shù),
- capacity(容量):在初始化hashmap時(shí)將會(huì)有一個(gè)默認(rèn)值(好象是10吧),隨著集合的大小也會(huì)自身調(diào)整。
- size(元素個(gè)數(shù)):有多少個(gè)元素size就是多少。
- load factor(負(fù)載因子):load factor=size/capacity,取值0~1。
當(dāng)負(fù)載因子很大時(shí),如有90個(gè)元素,而集合的容量為100,因子就是0.9,這樣情況非常不利于查詢操作,因?yàn)閜ut和get操作會(huì)遍歷大量的元素,時(shí)間復(fù)雜度無(wú)形就會(huì)增加,但在內(nèi)存開銷上確實(shí)是比較節(jié)省的,因?yàn)榧喜粫?huì)反復(fù)的創(chuàng)建,因?yàn)槊恳淮螖U(kuò)充集合的操作,就意味著要將原始元素重新插入到新的集合中去,性能開銷是很大的。
而當(dāng)負(fù)載因子很小時(shí),查詢效率將會(huì)非常高(因?yàn)楸闅v少),但是卻在內(nèi)部進(jìn)行了許多次開辟內(nèi)存的操作。
因此,在系統(tǒng)中,要根據(jù)實(shí)際需求正確把握hashmap的用法,如一開始建立集合的時(shí)候就知道這個(gè)集合非常大,那么就有必要在初始化的時(shí)候就指明capacity,不應(yīng)該使用默認(rèn)值,這樣效率能高點(diǎn);相反只有少量集合元素時(shí),不應(yīng)該在創(chuàng)建的時(shí)候指定很大的capacity,這明顯是在浪費(fèi)內(nèi)存。
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
原文鏈接:https://blog.csdn.net/kkkkkxiaofei/article/details/18041205