您的位置:首页 > 编程语言 > Java开发

java中的几个集合类

2017-01-12 16:31 190 查看

java中的几个集合类

今天在网上搜索了一下,发现一篇关于java集合的博文,里面整理得非常好, 特意copy过来和大家分享一下
本讲内容:集合 collection
讲集合collection之前,我们先分清三个概念:
colection 集合,用来表示任何一种数据结构
Collection 集合接口,指的是 java.util.Collection接口,是 Set、List 和 Queue 接口的超类接口
Collections 集合工具类,指的是 java.util.Collections 类。
 
SCJP考试要求了解的接口有:Collection ,Set ,SortedSet ,List ,Map ,SortedMap ,Queue ,NavigableSet ,NavigableMap,还有一个 Iterator 接口也是必须了解的。
SCJP考试要求了解的类有: HashMap ,Hashtable ,TreeMap ,LinkedHashMap ,HashSet ,LinkedHashSet ,TreeSet ,ArrayList ,Vector ,LinkedList ,PriorityQueuee ,Collections ,Arrays
下面给出一个集合之间的关系图:
上图中加粗线的ArrayList 和 HashMap 是我们重点讲解的对象。下面这张图看起来层级结构更清晰些。
我们这里说的集合指的是小写的collection,集合有4种基本形式,其中前三种的父接口是Collection。
List 关注事物的索引列表
Set 关注事物的唯一性
Queue 关注事物被处理时的顺序
Map 关注事物的映射和键值的唯一性
 
一、Collection 接口
Collection接口是 Set 、List 和 Queue 接口的父接口,提供了多数集合常用的方法声明,包括 add()、remove()、contains() 、size() 、iterator() 等。
add(E e)将指定对象添加到集合中
remove(Object o)将指定的对象从集合中移除,移除成功返回true,不成功返回false
contains(Object o)查看该集合中是否包含指定的对象,包含返回true,不包含返回flase
size()返回集合中存放的对象的个数。返回值为int
clear()移除该集合中的所有对象,清空该集合。
iterator()返回一个包含所有对象的iterator对象,用来循环遍历
toArray()返回一个包含所有对象的数组,类型是Object
toArray(T[] t)返回一个包含所有对象的指定类型的数组
 
 
我们在这里只举一个把集合转成数组的例子,因为Collection本身是个接口所以,我们用它的实现类ArrayList做这个例子:
01
import
java.util.ArrayList;
02
import
java.util.Collection;
03
 
04
public
class
CollectionTest {
05
 
06
    
public
static
void
main(String[] args) {
07
 
08
        
String a =
"a"
,b=
"b"
,c=
"c"
;
09
        
Collection list =
new
ArrayList();
10
        
list.add(a);
11
        
list.add(b);
12
        
list.add(c);
13
 
14
        
String[] array =  list.toArray(
new
String[
1
]);
15
 
16
        
for
(String s : array){
17
            
System.out.println(s);
18
        
}
19
    
}
20
}
编译并运行程序,检查结果:
二、几个比较重要的接口和类简介
1、List接口
List 关心的是索引,与其他集合相比,List特有的就是和索引相关的一些方法:get(int index) 、 add(int index,Object o) 、 indexOf(Object o) 。
ArrayList 可以将它理解成一个可增长的数组,它提供快速迭代和快速随机访问的能力。
LinkedList 中的元素之间是双链接的,当需要快速插入和删除时LinkedList成为List中的不二选择。
Vector 是ArrayList的线程安全版本,性能比ArrayList要低,现在已经很少使用
2、Set接口
Set关心唯一性,它不允许重复。
HashSet 当不希望集合中有重复值,并且不关心元素之间的顺序时可以使用此类。
LinkedHashset 当不希望集合中有重复值,并且希望按照元素的插入顺序进行迭代遍历时可采用此类。
TreeSet 当不希望集合中有重复值,并且希望按照元素的自然顺序进行排序时可以采用此类。(自然顺序意思是某种和插入顺序无关,而是和元素本身的内容和特质有关的排序方式,譬如“abc”排在“abd”前面。)
3、Queue接口
Queue用于保存将要执行的任务列表。
LinkedList 同样实现了Queue接口,可以实现先进先出的队列。
PriorityQueue 用来创建自然排序的优先级队列。番外篇中有个例子http://android.yaohuiji.com/archives/3454你可以看一下。
4、Map接口
Map关心的是唯一的标识符。他将唯一的键映射到某个元素。当然键和值都是对象。
HashMap 当需要键值对表示,又不关心顺序时可采用HashMap。
Hashtable 注意Hashtable中的t是小写的,它是HashMap的线程安全版本,现在已经很少使用。
LinkedHashMap 当需要键值对,并且关心插入顺序时可采用它。
TreeMap 当需要键值对,并关心元素的自然排序时可采用它。
三、ArrayList的使用
ArrayList是一个可变长的数组实现,读取效率很高,是最常用的集合类型。
1、ArrayList的创建
在Java5版本之前我们使用:
1
List list =
new
ArrayList();
在Java5版本之后,我们使用带泛型的写法:
1
List<String> list =
new
ArrayList<String>();
上面的代码定义了一个只允许保存字符串的列表,尖括号括住的类型就是参数类型,也成泛型。带泛型的写法给了我们一个类型安全的集合。关于泛型的知识可以参见这里。
2、ArrayList的使用:
01
List<String> list =
new
ArrayList<String>();
02
list.add(
"nihao!"
);
03
list.add(
"hi!"
);
04
list.add(
"konikiwa!"
);
05
list.add(
"hola"
);
06
list.add(
"Bonjour"
);
07
System.out.println(list.size());
08
System.out.println(list.contains(
21
));
09
System.out.println(list.remove(
"hi!"
));
10
System.out.println(list.size());
关于List接口中的方法和ArrayList中的方法,大家可以看看JDK中的帮助。
3、基本数据类型的的自动装箱:
我们知道集合中存放的是对象,而不能是基本数据类型,在Java5之后可以使用自动装箱功能,更方便的导入基本数据类型。
1
List<Integer> list =
new
ArrayList<Integer>();
2
list.add(
new
Integer(
42
));
3
list.add(
43
);
4、ArrayList的排序:
ArrayList本身不具备排序能力,但是我们可以使用Collections类的sort方法使其排序。我们看一个例子:
01
import
java.util.ArrayList;
02
import
java.util.Collections;
03
import
java.util.List;
04
 
05
public
class
Test {
06
 
07
    
public
static
void
main(String[] args) {
08
        
List<String> list =
new
ArrayList<String>();
09
        
list.add(
"nihao!"
);
10
        
list.add(
"hi!"
);
11
        
list.add(
"konikiwa!"
);
12
        
list.add(
"hola"
);
13
        
list.add(
"Bonjour"
);
14
 
15
        
System.out.println(
"排序前:"
+ list);
16
 
17
        
Collections.sort(list);
18
 
19
        
System.out.println(
"排序后:"
+ list);
20
    
}
21
 
22
}
编译并运行程序查看结果:
排序前:[nihao!,hi!,konikiwa!,hola,Bonjour]

排序后:[Bonjour,hi!,hola,konikiwa!,nihao!]
5、数组和List之间的转换
从数组转换成list,可以使用Arrays类的asList()方法:
01
import
java.util.ArrayList;
02
import
java.util.Collections;
03
import
java.util.List;
04
 
05
public
class
Test {
06
 
07
    
public
static
void
main(String[] args) {
08
 
09
            
String[] sa = {
"one"
,
"two"
,
"three"
,
"four"
};
10
            
List list =Arrays.asList(sa);
11
            
System.out.println(
"list:"
+list);
12
            
System.out.println(
"list.size()="
+list.size());
13
    
}
14
 
15
}
6、Iterator和for-each

在for-each出现之前,我们想遍历ArrayList中的每个元素我们会使用Iterator接口:
01
import
java.util.Arrays;
02
import
java.util.Iterator;
03
import
java.util.List;
04
 
05
public
class
Test {
06
 
07
    
public
static
void
main(String[] args) {
08
 
09
        
// Arrays类为我们提供了一种list的便捷创建方式
10
        
List<String> list =Arrays.asList(
"one"
,
"two"
,
"three"
,
"four"
);
11
 
12
        
// 转换成Iterator实例
13
        
Iterator<String> it = list.iterator();
14
 
15
        
//遍历
16
        
while
(it.hasNext()) {
17
            
System.out.println(it.next());
18
        
}
19
 
20
    
}
21
 
22
}
在for-each出现之后,遍历变得简单一些:
01
import
java.util.Arrays;
02
import
java.util.Iterator;
03
import
java.util.List;
04
 
05
public
class
Test {
06
 
07
    
public
static
void
main(String[] args) {
08
 
09
        
// Arrays类为我们提供了一种list的便捷创建方式
10
        
List<String> list =Arrays.asList(
"one"
,
"two"
,
"three"
,
"four"
);
11
 
12
        
for
(String s : list) {
13
            
System.out.println(s);
14
        
}
15
 
16
    
}
17
 
18
}
本讲内容:Map HashMap
前面课程中我们知道Map是个接口,它关心的是映射关系,它里面的元素是成对出现的,键和值都是对象且键必须保持唯一。这一点上看它和Collection是很不相同的。
一、Map接口
Map接口的常用方法如下表所示:
put(K key,V value)向集合中添加指定的键值对
putAll(Map <? extends K,? extends V> t)把一个Map中的所有键值对添加到该集合
containsKey(Object key)如果包含该键,则返回true
containsValue(Object value)如果包含该值,则返回true
get(Object key)根据键,返回相应的值对象
keySet()将该集合中的所有键以Set集合形式返回
values()将该集合中所有的值以Collection形式返回
remove(Object key)如果存在指定的键,则移除该键值对,返回键所对应的值,如果不存在则返回null
clear()移除Map中的所有键值对,或者说就是清空集合
isEmpty()查看Map中是否存在键值对
size()查看集合中包含键值对的个数,返回int类型
 
因为Map中的键必须是唯一的,所以虽然键可以是null,只能由一个键是null,而Map中的值可没有这种限制,值为null的情况经常出现,因此get(Object key)方法返回null,有两种情况一种是确实不存在该键值对,二是该键对应的值对象为null。为了确保某Map中确实有某个键,应该使用的方法是 containsKey(Object key) 。
二、HashMap
HashMap是最常用的Map集合,它的键值对在存储时要根据键的哈希码来确定值放在哪里。
1、HashMap的基本使用:
 
01
import
java.util.Collection;
02
import
java.util.HashMap;
03
import
java.util.Map;
04
import
java.util.Set;
05
 
06
public
class
Test {
07
 
08
    
public
static
void
main(String[] args) {
09
 
10
        
Map<Integer,String> map =
new
HashMap<Integer,String>();
11
 
12
        
map.put(
1
,
"白菜"
);
13
        
map.put(
2
,
"萝卜"
);
14
        
map.put(
3
,
"茄子"
);
15
        
map.put(
4
,
null
);
16
        
map.put(
null
,
null
);
17
        
map.put(
null
,
null
);
18
 
19
        
System.out.println(
"map.size()="
+map.size());
20
        
System.out.println(
"map.containsKey(1)="
+map.containsKey(
2
));
21
        
System.out.println(
"map.containsKey(null)="
+map.containsKey(
null
));
22
        
System.out.println(
"map.get(null)="
+map.get(
null
));
23
 
24
        
System.out.println(
"map.get(2)="
+map.get(
2
));
25
        
map.put(
null
,
"黄瓜"
);
26
        
System.out.println(
"map.get(null)="
+map.get(
null
));
27
 
28
        
Set set = map.keySet();
29
        
System.out.println(
"set="
+set);
30
 
31
        
Collection<String> c = map.values();
32
 
33
        
System.out.println(
"Collection="
+c);
34
 
35
    
}
36
 
37
}
编译并运行程序,查看结果:
1
map.size()=
5
2
map.containsKey(
1
)=
true
3
map.containsKey(
null
)=
true
4
map.get(
null
)=
null
5
map.get(
2
)=萝卜
6
map.get(
null
)=黄瓜
7
set=[
null
,
1
,
2
,
3
,
4
]
8
Collection=[黄瓜,白菜,萝卜,茄子,
null
]
2、HashMap 中作为键的对象必须重写Object的hashCode()方法和equals()方法
下面看一个我花了1个小时构思的例子,熟悉龙枪的朋友看起来会比较亲切,设定了龙和龙的巢穴,然后把它们用Map集合对应起来,我们可以根据龙查看它巢穴中的宝藏数量,例子只是为了说明hashCode这个知识点,所以未必有太强的故事性和合理性,凑合看吧:
01
import
java.util.HashMap;
02
import
java.util.Map;
03
 
04
public
class
Test {
05
 
06
    
public
static
void
main(String[] args) {
07
 
08
        
// 龙和它的巢穴映射表
09
        
Map<dragon ,Nest> map =
new
HashMap<dragon ,Nest>();
10
 
11
        
// 在Map中放入四只克莱恩大陆上的龙
12
        
map.put(
new
Dragon(
"锐刃"
,
98
),
new
Nest(
98
));
13
        
map.put(
new
Dragon(
"明镜"
,
95
),
new
Nest(
95
));
14
        
map.put(
new
Dragon(
"碧雷"
,
176
),
new
Nest(
176
));
15
        
map.put(
new
Dragon(
"玛烈"
,
255
),
new
Nest(
255
));
16
 
17
        
// 查看宝藏
18
        
System.out.println(
"碧雷巢穴中有多少宝藏:"
+ map.get(
new
Dragon(
"碧雷"
,
176
)).getTreasure());
19
    
}
20
 
21
}
22
 
23
// 龙
24
class
Dragon {
25
 
26
    
Dragon(String name,
int
level) {
27
        
this
.level = level;
28
        
this
.name = name;
29
    
}
30
 
31
    
// 龙的名字
32
    
private
String name;
33
 
34
    
// 龙的级别
35
    
private
int
level;
36
 
37
    
public
int
getLevel() {
38
        
return
level;
39
    
}
40
 
41
    
public
void
setLevel(
int
level) {
42
        
this
.level = level;
43
    
}
44
 
45
    
public
String getName() {
46
        
return
name;
47
    
}
48
 
49
    
public
void
setName(String name) {
50
        
this
.name = name;
51
    
}
52
 
53
}
54
 
55
// 巢穴
56
class
Nest {
57
 
58
    
//我研究的龙之常数
59
    
final
int
DRAGON_M =
4162
;
60
 
61
    
// 宝藏
62
    
private
int
treasure;
63
 
64
    
// 居住的龙的级别
65
    
private
int
level;
66
 
67
    
Nest(
int
level) {
68
        
this
.level = level;
69
        
this
.treasure = level * level * DRAGON_M;
70
    
}
71
 
72
    
int
getTreasure() {
73
        
return
treasure;
74
    
}
75
 
76
    
public
int
getLevel() {
77
        
return
level;
78
    
}
79
 
80
    
public
void
setLevel(
int
level) {
81
        
this
.level = level;
82
        
this
.treasure = level * level * DRAGON_M;
83
    
}
84
 
85
}
编译并运行查看结果:
1
Exception in thread
"main"
java.lang.NullPointerException
2
    
at Test.main(Test.java:
18
)
我们发现竟然报了错误,第18行出了空指针错误,也就是说get方法竟然没有拿到预期的巢穴对象。
在这里我们就要研究一下为什么取不到了。我们这里先解释一下HashMap的工作方式。

假设现在有个6张中奖彩票的存根,放在5个桶里(彩票首位只有1-5,首位是1的就放在一号桶,是2的就放在2号桶,依次类推),现在你拿了3张彩票来兑奖,一个号码是113,一个号码是213,一个号码是313。那么现在先兑第一张,取出一号桶里的存根发现存根号码和你的号码不符,所以你第一张没中奖。继续兑第二张,二号桶里就没存根所以就直接放弃了,把三号桶里的所有彩票存根都拿出来对应一番,最后发现有一个存根恰好是313,那么恭喜你中奖了。
HashMap在确定一个键对象和另一个键对象是否是相同时用了同样的方法,每个桶就是一个键对象的散列码值,桶里放的就是散列码相同的彩票存根,如果散列码不同,那么肯定没有相关元素存在,如果散列码相同,那么还要用键的equals()方法去比较是否相同,如果相同才认为是相同的键。简单的说就是 hashCode()相同 && equals()==true 时才算两者相同。
到了这里我们应该明白了,在没有重写一个对象的hashcode()和equals()方法之前,它们执行的是Object中对应的方法。而Object的hashcode()是用对象在内存中存放的位置计算出来的,每个对象实例都不相同。Object的equals()的实现更简单就是看两个对象是否==,也就是两个对象除非是同一个对象,否则根本不会相同。因此上面的例子虽然都是名字叫碧雷的龙,但是HashMap中却无法认可它们是相同的。
因此我们只有重写Key对象的hashCode()和equals()方法,才能避免这种情形出现,好在Eclipse可以帮我们自动生成一个类的hashCode()和equals(),我们把上面的例子加上这两个方法再试试看:
001
import
java.util.HashMap;
002
import
java.util.Map;
003
 
004
public
class
Test {
005
 
006
    
public
static
void
main(String[] args) {
007
 
008
        
// 龙和它的巢穴映射表
009
        
Map<dragon ,Nest> map =
new
HashMap<dragon ,Nest>();
010
 
011
        
// 在Map中放入四只克莱恩大陆上的龙
012
        
map.put(
new
Dragon(
"锐刃"
,
98
),
new
Nest(
98
));
013
        
map.put(
new
Dragon(
"明镜"
,
95
),
new
Nest(
95
));
014
        
map.put(
new
Dragon(
"碧雷"
,
176
),
new
Nest(
176
));
015
        
map.put(
new
Dragon(
"玛烈"
,
255
),
new
Nest(
255
));
016
 
017
        
// 查看宝藏
018
        
System.out.println(
"碧雷巢穴中有多少宝藏:"
+ map.get(
new
Dragon(
"碧雷"
,
176
)).getTreasure());
019
    
}
020
 
021
}
022
 
023
// 龙
024
class
Dragon {
025
 
026
    
Dragon(String name,
int
level) {
027
        
this
.level = level;
028
        
this
.name = name;
029
    
}
030
 
031
    
// 龙的名字
032
    
private
String name;
033
 
034
    
// 龙的级别
035
    
private
int
level;
036
 
037
    
public
int
getLevel() {
038
        
return
level;
039
    
}
040
 
041
    
public
void
setLevel(
int
level) {
042
        
this
.level = level;
043
    
}
044
 
045
    
public
String getName() {
046
        
return
name;
047
    
}
048
 
049
    
public
void
setName(String name) {
050
        
this
.name = name;
051
    
}
052
 
053
    
@Override
054
    
public
int
hashCode() {
055
        
final
int
PRIME =
31
;
056
        
int
result =
1
;
057
        
result =PRIME * result + level;
058
        
result =PRIME * result + ((name ==
null
) ?
0
: name.hashCode());
059
        
return
result;
060
    
}
061
 
062
    
@Override
063
    
public
boolean
equals(Object obj) {
064
        
if
(
this
== obj)
065
            
return
true
;
066
        
if
(obj ==
null
)
067
            
return
false
;
068
        
if
(getClass() != obj.getClass())
069
            
return
false
;
070
        
final
Dragon other = (Dragon) obj;
071
        
if
(level != other.level)
072
            
return
false
;
073
        
if
(name ==
null
) {
074
            
if
(other.name !=
null
)
075
                
return
false
;
076
        
}
else
if
(!name.equals(other.name))
077
            
return
false
;
078
        
return
true
;
079
    
}
080
 
081
}
082
 
083
// 巢穴
084
class
Nest {
085
 
086
    
//我研究的龙之常数
087
    
final
int
DRAGON_M =
4162
;
088
 
089
    
// 宝藏
090
    
private
int
treasure;
091
 
092
    
// 居住的龙的级别
093
    
private
int
level;
094
 
095
    
Nest(
int
level) {
096
        
this
.level = level;
097
        
this
.treasure = level * level * DRAGON_M;
098
    
}
099
 
100
    
int
getTreasure() {
101
        
return
treasure;
102
    
}
103
 
104
    
public
int
getLevel() {
105
        
return
level;
106
    
}
107
 
108
    
public
void
setLevel(
int
level) {
109
        
this
.level = level;
110
        
this
.treasure = level * level * DRAGON_M;
111
    
}
112
 
113
}
  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: