1、为什么要使用集合
要谈编程语言引入集合的原因,就不得不谈到集合与数组的差别:
(1)长度:数组是固定长度;集合是可变长度。
(2)元素性质:数组的元素可以是原生数据类型,也可以是引用类型;集合的元素只能是引用类型,原生数据类型必须装箱(jdk5.0之后有自动装箱和拆箱功能)之后才能放入集合之中。
(3)元素类型:数据在定义之初就确定了存储数据的类型,因此也只能存储单一一种数据类型;集合则可以存储不同的数据类型,虽然在实际应用中通常也只用于存储同一类型的数据。
2、集合的继承体系结构
Java中的集合主要集中在两部分—— java.util包和java.util.concurrent中。其中,后者是在前者的基础上,定义了一些实现了同步功能的集合。这里主要关注java.util包中的集合,可以简单地划分为三类:List、Set和Map。对应的UML图如下:
常用的ArrayList、LinkedList、Vector、HashSet、TreeSet都是Collection接口的具体实现类;而HashMap、TreeMap、Hashtable都是Map接口的实现类。因此,我们要讲集合,必须从Collection接口和Map接口入手。
3、Collection接口
Collection接口定义了集合常见的操作:
- 添加元素:add/addAll
- 删除元素:clear\remove/removeAll
- 判断是否包含某元素:contains/containsAll
- 判断集合是否为空:isEmpty
- 计算集合中元素的个数:size
- 将集合转换为数组:toArray
- 获取迭代器:iterator
集合的遍历是实际开发过程中常见的需求。这里我们以Collection的具体实现类ArrayList为例,介绍集合的三种遍历方法,并在集合遍历的基础上将上述主要的集合操作方法进行演示。具体如下:
package test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest
{
public static void main(String[] args)
{
//创建集合对象
Collection<String> list = new ArrayList<String>();
//添加元素
list.add("string1");
list.add("string2");
list.add("string3");
list.add("string4");
list.add("string5");
//方法一:用增强的for循环遍历集合
for(String element :list)
{
System.out.println(element);
}
//删除元素
list.remove("string5");
System.out.println("--------------------");
//判断集合中是否包含“string5”
System.out.println(list.contains("string5"));
//判断集合是否为空
System.out.println(list.isEmpty());
//计算集合中的元素个数
int number = list.size();
System.out.println(number);
System.out.println("--------------------");
//方法二:数组遍历法,将集合转化为数组,基于数组遍历集合
//首先,将集合转化为数组
Object[] array = list.toArray();
//然后,用普通的for循环遍历集合(当然也可以用增强的for循环)
for(Object element : array)
{
System.out.println(element);
}
System.out.println("--------------------");
//方法三:采用迭代器遍历数组
for(Iterator<String> iterator = list.iterator(); iterator.hasNext();)
{
String string = iterator.next();
System.out.println(string);
}
}
}
4、List子接口
ArrayList、LinkedList、Vector等具体类并不直接实现Collection接口,而是实现Collection的子接口List。List子接口定义了Collection所不具备的功能,其特点是有序、可重复。List的特有功能包括:
- 在指定位置添加元素:add
- 删除索引元素: remove
- 获取制定位置的元素:get
- 修改元素:set
- 获取迭代器:ListIterator
具体实现类及其特点简单概括如下:
(1)ArrayList:底层数据结构是数组,查询快,增删慢;
(2)Vector :底层数据结构是数组,查询快,增删慢,线程安全,效率低;
(3)LinkedList:底层数据结构是链表,查询慢,增删快,线程不安全,效率高。
5、Set子接口
同样,HashSet、TreeSet等具体类也并不直接实现Collection接口,而是实现Collection的子接口Set。Set子接口定义了Collection所不具备的功能,其特点是无序、唯一。
Set的具体实现类及其特点简单概括如下:
(1)HashSet:底层数据结构是哈希表;依赖hashCode()和equals()两个方法;
(2)LinkedHashSet:底层数据结构是链表和哈希表,由链表保证元素有序,由哈希表保证元素唯一;
(3)TreeSet:底层数据结构是红黑树,保证元素排序的方式自然排序、比较器排序,根据比较的返回值是否是0来决定元素的唯一性。
6、Map接口
Map存储的是键值对形式的元素。键唯一,值可以重复。Map的主要功能总结如下:
添加元素:put
删除元素:clear/remove
判断是否包含值\键:containsValue\ containsKey
判断是否为空:isEmpty
获取值\键的集合:get\keySet\values
获取长度:size
在此以HashMap为例演示集合的遍历:
package test;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class MapTest
{
public static void main(String[] args)
{
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "string1");
map.put(2, "string2");
map.put(3, "string3");
map.put(4, "string4");
map.put(5, "string5");
//遍历方法一:以键找值
Set<Integer> set = map.keySet();
for(Integer key : set)
{
String value = map.get(key);
System.out.println(key + " : " + value);
}
System.out.println("--------------------");
//删除元素
map.remove(2);
//判断是否为空
System.out.println(map.isEmpty());
//获取长度
System.out.println(map.size());
System.out.println("--------------------");
//遍历方法二:键值对对象
Set<Entry<Integer, String>> set2 = map.entrySet();
for(Map.Entry<Integer, String> entry : set2)
{
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " + " + value);
}
}
}
Map的具体实现类及其特点简单概括如下:
(1)HashMap:基于哈希表的Map接口实现,哈希表的作用是用来保证键的唯一性的,要保证唯一性必须重写hashCode()和equals()方法;
(2)LinkedHashMap:基于哈希表和链接列表实现,具有可预知的迭代顺序,由哈希表保证键的唯一性,由链表保证键的有序性
(3)TreeMap:是基于红黑树的Map接口的实现,保证元素排序的方式有自然排序和比较器排序。