java中的几个集合类

 
       
       
       
       
       
       
       

java中的几个集合类

今天在网上搜索了一下,发现一篇关于java集合的博文,里面整理得非常好, 特意copy过来和大家分享一下

本讲内容:集合 collection

讲集合collection之前,我们先分清三个概念:

  1. colection 集合,用来表示任何一种数据结构
  2. Collection 集合接口,指的是 java.util.Collection接口,是 Set、List 和 Queue 接口的超类接口
  3. 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。

  1. List 关注事物的索引列表
  2. Set 关注事物的唯一性
  3. Queue 关注事物被处理时的顺序
  4. 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 }
   
时间: 2024-09-19 11:51:27

java中的几个集合类的相关文章

java中的集合类 以及 迭代器

  /* java中的集合类   实现了 Connection接口 ArrayList这个集合类  java.util.*这个包的类  toArray()方法可以返回一个 Object类型的对象数组     我们部队 ArratList进行泛型规范那么就会警告 但是还是可以运行的 这是因为JDK1.5中引进了泛型,但是你的ArrayList却没有采用,所有会有安全问题,比如你可以把一个别的对象放入al里面去,但是取出来的时候可能就会出错了,所以为了安全,在JDK1.5中引进了泛型这个概念. 你可

集合类-java中为什么不直接用Collection接口,而是还要再造一个他们相应的抽象类?

问题描述 java中为什么不直接用Collection接口,而是还要再造一个他们相应的抽象类? 诸如HashMap.ArrayList等具体类,都是直接继承自AbstractMap或AbstractList,为什么要设置这么个抽象类,而不直接用相应的接口? 还有这些抽象类为什么单单空着size和iterator两个方法不去实现? 解决方案 抽象类可以添加公共方法,一些可以写的代码可以先写好,接口没办法写方法. 解决方案二: public interface Collection extends

Java中List的使用方法简单介绍_java

Java中的List就是一种集合对象,将所有的对象集中到一起存储.List里面可以放任意的java对象,也可以直接放值. 使用方法很简单,类似于数组.  使用List之前必须在程序头引入java.util.*  import java.util.*; public class list { public static void main(String args[]) { List a=new ArrayList(); a.add(1);//在LIST a中添加1 System.out.print

详解java中的Collections类_java

一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数组无法完全表达你要表达的东西,而定义一个类Class有太过麻烦,这时候,你可以考虑一下用Java中的Collections类.使用Collections类,必须在文件头声明import java.util.*; 一.动态.有序.可变大小的一维数组Vector与ArrayList  Collectio

java中一个商品列表集合简单问题

问题描述 java中一个商品列表集合简单问题 java中一个商品列表集合简单问题 java中一个商品列表集合简单问题 肯德可以理解为对象,java一切都是对象 那么可以理解为一个类吗可以理解为一个数组吗,可以理解为一种数据泪腺吗 解决方案 对象集合类是类,但不能说对象集合的对象是一个类,对象就是类的实例,和类是不等的.数组是指基本数据类型集合.比如int [] arrs = new int[]{},而List 等类创建的对象集只能称为集合.不能理解为数据类型.数据类型只有基本类型和引用类型.

JAVA中的内存溢出

一.概述 内存溢出与数据库锁表的问题,可以说是开发人员的噩梦,一般的程序异常,总是可以知道在什么时候或是在什么操作步骤上出现了异常,而且根据堆栈信息也很容易定位到程序中是某处出现了问题.内存溢出与锁表则不然,一般现象是操作一般时间后系统越来越慢,直到死机,但并不能明确是在什么操作上出现的,发生的时间点也没有规律,查看日志或查看数据库也不能定位出问题的代码. 更严重的是内存溢出与数据库锁表在系统开发和单元测试阶段并不容易被发现,当系统正式上线一般时间后,操作的并发量上来了,数据也积累了一些,系统就

java中同一个方法可以被几个类或者被几个对象共享

问题描述 java中同一个方法可以被几个类或者被几个对象共享 java中同一个方法可以被几个类或者被几个对象共享java中同一个方法可以被几个类或者被几个对象共享 解决方案 方法本质上是字节码序列,底层是指令集合.方法是通过对象调用的,对象是存储在堆中的,是所有线程共享的.方法是类型信息,不存在共享这个说法. 解决方案二: java 中的方法都是在类中的,而对于你说的方法可以被几个类或者几个对象共享,这个问题问的没有多大意义,方法属于类的方法,不会被共享,可以被子类复写和调用. 解决方案三: j

Java 中泛型的全面解析(转)

Java泛型(generics) 是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter).声明的类型参数在使用时用具体的类型来替换.泛型最主要的应用是在JDK 5中的新集合类框架中.对于泛型概念的引入,开发社区的观点是褒贬不一.从好的方面来说,泛型的引入可以解决之前的集合类框架在使用过程中通常会出现的运行时刻类型错误,因为编译器可以在编译时刻就发现很多明显的错误.而从不好的地方来说,为了保证与旧有版本的兼容性,Java泛型的实现上存在着一些不够优雅的

java中treemap和treeset实现(红黑树)

TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 TreeMap 的关系 为了让大家了解 TreeMap 和 TreeSet 之间的关系,下面先看 TreeSet 类的部分源代码: public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializab