Java集合概述

容器,就是可以容纳其他Java对象的对象。Java Collections Framework(JCF)为Java开发者提供了通用的容器,其始于JDK 1.2,优点是:
  • 降低编程难度
  • 提高程序性能
  • 提高API间的互操作性
  • 降低学习难度
  • 降低设计和实现相关API的难度
  • 增加程序的重用性

Java容器里只能放对象,对于基本类型(int, long, float, double等),需要将其包装成对象类型后(Integer, Long, Float, Double等)才能放到容器里。很多时候拆包装和解包装能够自动完成。这虽然会导致额外的性能和空间开销,但简化了设计和编程。

Java集合类基本概念

在编程中,常常需要集中存放多个数据。从传统意义上讲,数组是我们的一个很好的选择,前提是我们事先已经明确知道我们将要保存的对象的数量。一旦在数组初始化时指定了这个数组长度,这个数组长度就是不可变的,如果我们需要保存一个可以动态增长的数据(在编译时无法确定具体的数量),java的集合类就是一个很好的设计方案了。

集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所以的集合类都位于java.util包下,后来为了处理多线程环境下的并发安全问题,java5还在java.util.concurrent包下提供了一些多线程支持的集合类。

在学习Java中的集合类的API、编程原理的时候,我们一定要明白,”集合”是一个很古老的数学概念,它远远早于Java的出现。从数学概念的角度来理解集合能帮助我们更好的理解编程中什么时候该使用什么类型的集合类。

Java容器类类库的用途是”保存对象”,并将其划分为两个不同的概念:

复制代码
1) Collection
一组"对立"的元素,通常这些元素都服从某种规则
   1.1) List必须保持元素特定的顺序
   1.2) Set不能有重复元素
   1.3) Queue保持一个队列(先进先出)的顺序
2) Map
一组成对的"键值对"对象
复制代码

Collection和Map的区别在于容器中每个位置保存的元素个数:

1) Collection 每个位置只能保存一个元素(对象)
2) Map保存的是"键值对",就像一个小型数据库。我们可以通过"键"找到该键对应的"值"

Java集合类架构层次关系

复制代码
1. Interface Iterable
迭代器接口,这是Collection类的父接口。实现这个Iterable接口的对象允许使用
foreach进行遍历,也就是说,所有的Collection集合对象都具有"foreach可遍历性"。
这个Iterable接口只有一个方法: iterator()。
它返回一个代表当前集合对象的泛型<T>迭代器,用于之后的遍历操作
1.1 Collection
Collection是最基本的集合接口,一个Collection代表一组Object的集合,这些Object
被称作Collection的元素。Collection是一个接口,用以提供规范定义,不能被实例化
使用
    1) Set
    Set集合类似于一个罐子,"丢进"Set集合里的多个对象之间没有明显的顺序。Set继承
自Collection接口,不能包含有重复元素(记住,这是整个Set类层次的共有属性)。
    Set判断两个对象相同不是使用"=="运算符,而是根据equals方法。也就是说,我们在
加入一个新元素的时候,如果这个新元素对象和Set中已有对象进行注意equals比较都返回
false,则Set就会接受这个新元素对象,否则拒绝。
    因为Set的这个制约,在使用Set集合的时候,应该注意两点:1) 为Set集合里的元素
的实现类实现一个有效的equals(Object)方法、2) 对Set的构造函数,传入的
Collection参数不能包含重复的元素
        1.1) HashSet
        HashSet是Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,
因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该
对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象
在HashSet中的存储位置。
        值得主要的是,HashSet集合判断两个元素相等的标准是两个对象通过equals()
方法比较相等,并且两个对象的hashCode()方法的返回值相等
            1.1.1) LinkedHashSet
            LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,
但和HashSet不同的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的
顺序保存的。
       当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添
加顺序来访问集合里的元素。
            LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能
,但在迭代访问Set里的全部元素时(遍历)将有很好的性能(链表很适合进行遍历)
        1.2) SortedSet    
        此接口主要用于排序操作,即实现此接口的子类都属于排序的子类
            1.2.1) TreeSet
            TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序
状态
        1.3) EnumSet
        EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有元素都必须是指定枚
举类型的枚举值,该枚举类型在创建EnumSet时显式、或隐式地指定。EnumSet的集合元素
也是有序的,它们以枚举值在Enum类内的定义顺序来决定集合元素的顺序
    2) List
    List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。
List集合允许加入重复元素,因为它可以通过索引来访问指定位置的集合元素。List集合
默认按元素的添加顺序设置元素的索引
        2.1) ArrayList
        ArrayList是基于数组实现的List类,它封装了一个动态的增长的、允许再分配
的Object[]数组。
        2.2) Vector
        Vector和ArrayList在用法上几乎完全相同,但由于Vector是一个古老的集合,
所以Vector提供了一些方法名很长的方法,但随着JDK1.2以后,java提供了系统的集合框
架,就将Vector改为实现List接口,统一归入集合框架体系中
            2.2.1) Stack
            Stack是Vector提供的一个子类,用于模拟"栈"这种数据结构(LIFO后进
先出)
        2.3) LinkedList
        implements List<E>, Deque<E>。实现List接口,能对它进行队列操作,
即可以根据索引来随机访问集合中的元素。同时它还实现Deque接口,即能将LinkedList当
作双端队列使用。自然也可以被当作"栈来使用"
    3) Queue
    Queue用于模拟"队列"这种数据结构(先进先出 FIFO)。队列的头部保存着队列中存放
时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列
的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问队列中的元素。
结合生活中常见的排队就会很好理解这个概念
        3.1) PriorityQueue
        PriorityQueue并不是一个比较标准的队列实现,PriorityQueue保存队列元素
的顺序并不是按照加入队列的顺序,而是按照队列元素的大小进行重新排序,这点从它的类名
也可以看出来
        3.2) Deque
        Deque接口代表一个"双端队列",双端队列可以同时从两端来添加、删除元素,因
此Deque的实现类既可以当成队列使用、也可以当成栈使用
            3.2.1) ArrayDeque
            是一个基于数组的双端队列,和ArrayList类似,它们的底层都采用一个动
态的、可重分配的Object[]数组来存储集合元素,当集合元素超出该数组的容量时,系统会
在底层重新分配一个Object[]数组来存储集合元素
            3.2.2) LinkedList
1.2 Map
Map用于保存具有"映射关系"的数据,因此Map集合里保存着两组值,一组值用于保存Map里
的key,另外一组值用于保存Map里的value。key和value都可以是任何引用类型的数据。
Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较结果总是返回
false。
关于Map,我们要从代码复用的角度去理解,java是先实现了Map,然后通过包装了一个所有
value都为null的Map就实现了Set集合
Map的这些实现类和子接口中key集的存储形式和Set集合完全相同(即key不能重复)
Map的这些实现类和子接口中value集的存储形式和List非常类似(即value可以重复、根据
索引来查找)
    1) HashMap
    和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。
并且类似于HashSet判断两个key是否相等的标准也是: 两个key通过equals()方法比较
返回true、
   同时两个key的hashCode值也必须相等
        1.1) LinkedHashMap
        LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护
Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进
行排序进行区分)
    2) Hashtable
    是一个古老的Map实现类
        2.1) Properties 
        Properties对象在处理属性文件时特别方便(windows平台上的.ini文件),
Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value
对写入到属性文件中,也可以把属性文件中的"属性名-属性值"加载到Map对象中
    3) SortedMap
    正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类一样,
Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类
        3.1) TreeMap
        TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。
TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所
有的key-value对处于有序状态。同样,TreeMap也有两种排序方式: 自然排序、定制排序
    4) WeakHashMap
    WeakHashMap与HashMap的用法基本相似。区别在于,HashMap的key保留了对实际对
象的"强引用",这意味着只要该HashMap对象不被销毁,该HashMap所引用的对象就不会被
垃圾回收。
  但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象
的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,
当垃  圾回收了该key所对应的实际对象之后,WeakHashMap也可能自动删除这些key所
对应的key-value对
    5) IdentityHashMap
    IdentityHashMap的实现机制与HashMap基本相似,在IdentityHashMap中,当且
仅当两个key严格相等(key1 == key2)时,IdentityHashMap才认为两个key相等
    6) EnumMap
    EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举
类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap根据key的自然
顺序  (即枚举值在枚举类中的定义顺序)
复制代码

补充知识:

泛型(Generics)

Java容器能够容纳任何类型的对象,这一点表面上是通过泛型机制完成,Java泛型不是什么神奇的东西,只是编译器为我们提供的一个“语法糖”,泛型本身并不需要Java虚拟机的支持,只需要在编译阶段做一下简单的字符串替换即可。实质上Java的单继承机制才是保证这一特性的根本,因为所有的对象都是Object的子类,容器里只要能够存放Object对象就行了。
事实上,所有容器的内部存放的都是Object对象,泛型机制只是简化了编程,由编译器自动帮我们完成了强制类型转换而已。JDK 1.4以及之前版本不支持泛型,类型转换需要程序员显式完成。

 

 

迭代器(Iterator)

跟C++ STL一样,JCF的迭代器(Iterator)为我们提供了遍历容器中元素的方法。只有容器本身清楚容器里元素的组织方式,因此迭代器只能通过容器本身得到。每个容器都会通过内部类的形式实现自己的迭代器。相比STL的迭代器,JCF的迭代器更容易使用。

//visit a list with iterator
ArrayList<String> list = new ArrayList<String>();
list.add(new String("Monday"));
list.add(new String("Tuesday"));
list.add(new String("Wensday"));
Iterator<String> it = list.iterator();//得到迭代器
while(it.hasNext()){
    String weekday = it.next();//访问元素
    System.out.println(weekday.toUpperCase());
}

JDK 1.5 引入了增强的for循环,简化了迭代容器时的写法。

//使用增强for迭代
ArrayList<String> list = new ArrayList<String>();
list.add(new String("Monday"));
list.add(new String("Tuesday"));
list.add(new String("Wensday"));
for(String weekday : list){//enhanced for statement
    System.out.println(weekday.toUpperCase());
}
参考:

 http://www.cnblogs.com/CarpenterLee/p/5414253.html

http://www.cnblogs.com/LittleHann/p/3690187.html

此条目发表在Java基础分类目录,贴了标签。将固定链接加入收藏夹。

发表评论

邮箱地址不会被公开。 必填项已用*标注