在 JDK API 中专门设计了一组类,这组类的功能就是实现各种各样方式的数据存储,这样一组专门用来存储其它对象的类,一般被称为对象容器类,简称容器类,这组类和接口的设计结构也被统称为集合框架 (Collection Framework) 。
这组类和接口都包含在 java.util 包中。
为了使整个集合框架中的类便于使用,在设计集合框架时大量的使用接口,实际实现的功能类实现对应的接口,这样可以保证各个集合类的使用方式保持统一。
在集合框架中,提供的存储方式共有两种:
1 、按照索引值操作数据
在这种存储方式中,为每个存储的数据设定一个索引值,存储在容器中的第一个元素索引值是 0 ,第二个索引值是 1 ,依次类推。在操作数据时按照索引值操作对应的数据,实现这种方式的集合类都实现 java.util.Collection 接口。
2 、按照名称操作数据
在这种存储方式中,为每个存储的数据设定一个名称 ( 任意非 null 的对象都可以作为名称 ) ,以后按照该名称操作该数据,要求名称不能重复,每个名称对应唯一的一个值。这种存储数据的方式也称作名称 - 数值对,也就是名值对存储。实现这种方式的几个类都实现 java.util.Map 接口。
这里“按照索引值操作数据”的存储方式,又按照容器内部是否能够存储重复的元素,划分成两类:
1 、允许存储重复元素。
这种存储方式中,所有的类都实现了 java.util.List 接口。
2 、不允许存储重复元素。
这种存储方式中,所有的类都实现了 java.util.Set 接口。
这样,集合框架中的类就分成了三大类:
1 、 List 系列
该系列中的类按照索引值来操作数据,允许存放重复的元素。
2 、 Set 系列
该系列中的类按照索引值来操作数据,不允许存放重复的元素。
3 、 Map 系列
该系列中的类按照名称来操作数据,名称不允许重复,值可以重复,一个名称对应一个唯一的值。
而 在数据结构中,实现数据的存储又可以使用不同的数据结构类型进行存储,例如数组、链表、栈、队列和树等,则以上三类集合框架可以使用不同的数据结构类进行 实现,使用每种数据结构则具备该中数据结构的特点。例如使用数组则访问速度快,使用链表则便于动态插入和删除等,这样就造成了集合框架的复杂性。
另外,在将对象存储到集合类中,为了加快存储的速度,要求被存储对象的类中必须覆盖 equals 方法和 hashCode 方法。
对于这些集合类,下面按照以上三个系列的顺序一一进行说明。
9.6.3.1 List 系列
List 系列的类均实现 List 接口,大部分的类都以 List 作为类名的后缀,也有部分该体系中的类命名比较特殊。
该系列中的类,比较常见的有 ArrayList 和 LinkedList 两个。其中 ArrayList 是以数组为基础实现的 List ,而 LinkedList 则是以链表为基础实现的 List , ArrayList 拥有数组的优点,而 LinkedList 拥有链表的优点。
由于该体系中的类均实现 List 接口,所以在这些类的内部,相同的功能方法声明是保持一致的,下面进行一一介绍:
a 、 add 方法
boolean add(Object o)
该方法的作用是追加对象 o 到已有容器的末尾。
另外一个 add 方法:
void add(int index, Object element)
该方法的作用是将对象 element 插入到容器中索引值为 index 的位置,原来位于该位置的对象以及后续的内容将依次向后移动。
b 、 addAll 方法
boolean addAll(Collection c)
该方法的作用是将容器对象 c 中的每个元素依次添加到当前容器的末尾。
另外一个 addAll 方法:
boolean addAll(int index, Collection c)
该方法的作用是将容器对象 c 中的第一个元素插入到当前容器中索引值为 index 的位置,第二个元素插入到当前容器中索引值为 index+1 的位置,依次类推。而当前容器中原来位于 index 以及 index 索引值以后的元素则依次向后移动。
c 、 get 方法
Object get(int index)
该方法的作用是返回当前容器对象中索引值为 index 的元素的内容。
d 、 indexOf 方法
int indexOf(Object o)
该方法的作用是查找当前容器中是否存在对象 o ,如果存在则返回该对象第一次出现位置的索引值,如果不存在则返回 -1 。
另外一个方法 lastIndexOf 则是从末尾向前查找,返回从末尾向前第一次出现位置的索引值,如果不存在则返回 -1 。
e 、 remove 方法
Object remove(int index)
该方法的作用是删除索引值为 index 的对象的内容,如果删除成功则返回被删除对象的内容。
另外一个 remove 方法:
boolean remove(Object o)
该方法的作用是删除对象内容为 o 的元素,如果相同的对象有多个,则只删除索引值小的对象。如果删除成功则返回 true ,否则返回 false 。
无论使用哪一个 remove 方法,类内部都自动移动将被删除位置后续的所有元素向前移动,保证索引值的连续性。
f 、 set 方法
Object set(int index, Object element)
该方法的作用是修改索引值为 index 的内容,将原来的内容修改成对象 element 的内容。
g 、 size 方法
int size()
该方法的作用是返回当前容器中已经存储的有效元素的个数。
h 、 toArray 方法
Object[] toArray()
该方法的作用是将当前容器中的元素按照顺序转换成一个 Object 数组。
下面是一个简单的以 ArrayList 类为基础实现的 List 系列中类基本使用的示例,代码如下:
import java.util.*;
/**
* 以 ArrayList 类为基础演示 List 系列类的基本使用
*/
public class ArrayListUse {
public static void main(String[] args) {
// 容器对象的初始化
List list = new ArrayList();
// 添加数据
list.add("1");
list.add("2");
list.add("3");
list.add("1");
list.add("1");
// 插入数据
list.add(1,"12");
// 修改数据
list.set(2, "a");
// 删除数据
list.remove("1");
// 遍历
int size = list.size(); // 获得有效个数
// 循环有效索引值
for(int i = 0;i < size;i++){
System.out.println((String)list.get(i));
}
}
}
该程序的运行结果为:
12
a
3
1
1
在 List 系列中,还包含了 Stack( 栈 ) 类和 Vector( 向量 ) 类, Stack 类除了实现 List 系列的功能以外,还实现了栈的结构,主要实现了出栈的 pop 方法和入栈的 push 方法。
而 Vector 类由于需要兼容老版本 JDK 中缘故,所以在实现的方法中需要提供老版本 Vector 类中对应的方法,这样导致 Vector 类中相同或类似的功能方法一般是成对出现的。
Set 系列
Set 系列中的类都实现了 Set 接口,该系列中的类均以 Set 作为类名的后缀。该系列中的容器类,不允许存储重复的元素。也就是当容器中已经存储一个相同的元素时,无法实现添加一个完全相同的元素,也无法将已有的元素修改成和其它元素相同。
Set 系列中类的这些特点,使得在某些特殊场合的使用比较适合。
该系列中常见的类有:
1 、 CopyOnWriteArraySet
以数组为基础实现的 Set 类。
2 、 HashSet
以哈希表为基础实现的 Set 类。
3 、 LinkedHashSet
以链表为基础实现的 Set 类。
4 、 TreeSet
以树为基础实现的 Set 类。
以不同的数据结构类型实现的 Set 类,拥有不同数据结构带来的特性,在实际使用时,根据逻辑的需要选择合适的 Set 类进行使用。
Set 系列中的类的方法和 List 系列中的类的方法要比 List 系列中少很多,例如不支持插入和修改,而且对于 Set 系列中元素的遍历也需要转换为专门的 Iterator( 迭代器 ) 对象才可以进行遍历,遍历时顺序和 Set 中存储的顺序会有所不同。
下面是以 HashSet 类为基础实现的示例代码,代码如下:
import java.util.*;
/**
* 以 HashSet 为基础演示 Set 系列类的基本使用
*/
public class HashSetUse {
public static void main(String[] args) {
// 容器对象的初始化
Set set = new HashSet();
// 添加元素
set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.add("1");
// 删除数据
//set.remove("1");
// 遍历
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println((String)iterator.next());
}
}
}
该程序的运行结果为:
3
2
1
Map 系列
Map 系列中的类都实现了 Map 接口,该系列中的部分类以 Map 作为类名的后缀。该系列容器类存储元素的方式和以上两种完全不同。
Map 提供了一种使用“名称:值”这样的名称和数值对存储数据的方法,在该存储方式中,名称不可以重复,而不同的名称中可以存储相同的数值。具体这种存储的格式将在示例代码中进行实现。
在这种存储结构中,任何不为 null 的对象都可以作为一个名称 (key) 来作为存储的值 (value) 的标识,使用这种形式更利于存储比较零散的数据,也方便数据的查找和获得。 Map 类中存储的数据没有索引值,系统会以一定的形式索引存储的名称,从而提高读取数据时的速度。
该系列中常见的类有:
1 、 HashMap
以 Hash( 哈希表 ) 为基础实现的 Map 类。
2 、 LinkedHashMap
以链表和 Hash( 哈希表 ) 为基础实现的 Map 类。
3 、 TreeMap
以树为基础实现的 Map 类。
和上面的结构类似,以不同的数据结构实现的 Map 类,拥有不同数据结构的特点,在实际的项目中使用时,根据需要选择合适的即可。
该系列的类中常见的方法如下:
a 、 get 方法
Object get(Object key)
该方法的作用是获得当前容器中名称为 key 的结构对应的值。
b 、 keySet 方法
Set keySet()
该方法的作用是返回当前容器中所有的名称,将所有的名称以 Set 的形式返回。使用这个方法可以实现对于 Map 中所有元素的遍历。
c 、 put 方法
Object put(Object key, Object value)
该方法的作用是将值 value 以名称 key 的形式存储到容器中。
d 、 putAll 方法
void putAll(Map t)
该方法的作用是将 Map 对象 t 中的所有数据按照原来的格式存储到当前容器类中,相当于合并两个 Map 容器对象。
e 、 remove 方法
Object remove(Object key)
该方法的作用是删除容器中名称为 key 的值。
f 、 size 方法
int size()
该方法的作用是返回当前日期中存储的名称:值数据的组数。
g 、 values 方法
Collection values()
该方法的作用是返回当前容器所有的值组成的集合,以 Collection 对象的形式返回。
下面是一个简单的示例,在该示例中演示 Map 系列类的基本使用,代码如下:
import java.util.*;
/**
* 以 HashMap 为基础演示 Map 系列中类的使用
*/
public class HashMapUse {
public static void main(String[] args) {
// 容器对象的初始化
Map map = new HashMap();
// 存储数据
map.put(" 苹果 ", "2.5");
map.put(" 桔子 ", "2.5");
map.put(" 香蕉 ", "3");
map.put(" 菠萝 ", "2");
// 删除元素
map.remove(" 桔子 ");
// 修改元素的值
map.put(" 菠萝 ", "5");
// 获得元素个数
int size = map.size();
System.out.println(" 个数是: " + size);
// 遍历 Map
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
// 获得名称
String name = (String)iterator.next();
// 获得数值
String value = (String)map.get(name);
// 显示到控制台
System.out.println(name + ":" + value);
}
}
}
该程序的运行结果为:
个数是: 3
香蕉 :3
菠萝 :5
苹果 :2.5