JAVA集合类框架

为什么要使用集合类

当你事先不知道要存放数据的个数,或者你需要一种比数组下标存取机制更灵活的方法时,你就需要用到集合类。

理解集合类

集合类存放于java.util包中。
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。
集合类型主要有3种:set(集)、list(列表)和map(映射)。

(1)
集(set)是最简单的一种集合,它的对象不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
对集中成员的访问和操作是通过集中对象的引用进行的,所以集中不能有重复对象。
集也有多种变体,可以实现排序等功能,如TreeSet,它把对象添加到集中的操作将变为按照某种比较规则将其插入到有序的对象序列中。它实现的是SortedSet接口,也就是加入了对象比较的方法。通过对集中的对象迭代,我们可以得到一个升序的对象集合。

(2)列表
列表的主要特征是其对象以线性方式存储,没有特定顺序,只有一个开头和一个结尾,当然,它与根本没有顺序的集是不同的。
列表在数据结构中分别表现为:数组和向量、链表、堆栈、队列。
关于实现列表的集合类,是我们日常工作中经常用到的,将在后边的笔记详细介绍。

(3)映射
映射与集或列表有明显区别,映射中每个项都是成对的。映射中存储的每个对象都有一个相关的关键字(Key)对象,关键字决定了对象在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的。
关键字本身并不能决定对象的存储位置,它需要对过一种散列(hashing)技术来处理,产生一个被称作散列码(hash code)的整数值,散列码通常用作一个偏置量,该偏置量是相对于分配给映射的内存区域起始位置的,由此确定关键字/对象对的存储位置。理想情况下,散列 处理应该产生给定范围内均匀分布的值,而且每个关键字应得到不同的散列码。

集合类简介

java.util中共有13个类可用于管理集合对象,它们支持集、列表或映射等集合,以下是这些类的简单介绍

集:
HashSet:
使用HashMap的一个集的实现。虽然集定义成无序,但必须存在某种方法能相当高效地找到一个对象。使用一个HashMap对象实现集的存储和检索操作是在固定时间内实现的.
TreeSet: 在集中以升序对对象排序的集的实现。这意味着从一个TreeSet对象获得第一个迭代器将按升序提供对象。TreeSet类使用了一个TreeMap.
列表:
Vector:
实现一个类似数组一样的表,自动增加容量来容纳你所需的元素。使用下标存储和检索对象就象在一个标准的数组中一样。你也可以用一个迭代器从一个Vector中检索对象。Vector是唯一的同步容器类??当两个或多个线程同时访问时也是性能良好的。
Stsck: 这个类从Vector派生而来,并且增加了方法实现栈??一种后进先出的存储结构。
LinkedList: 实现一个链表。由这个类定义的链表也可以像栈或队列一样被使用。
ArrayList: 实现一个数组,它的规模可变并且能像链表一样被访问。它提供的功能类似Vector类但不同步。


映射:
HashTable:
实现一个映象,所有的键必须非空。为了能高效的工作,定义键的类必须实现hashcode()方法和equal()方法。这个类是前面java实现的一个继承,并且通常能在实现映象的其他类中更好的使用。
HashMap: 实现一个映象,允许存储空对象,而且允许键是空(由于键必须是唯一的,当然只能有一个)。
WeakHashMap: 实现这样一个映象:通常如果一个键对一个对象而言不再被引用,键/对象对将被舍弃。这与HashMap形成对照,映象中的键维持键/对象对的生命周期,尽管使用映象的程序不再有对键的引用,并且因此不能检索对象。
TreeMap: 实现这样一个映象,对象是按键升序排列的。

Set和List都是由公共接口Collection扩展而来,所以它们都可以使用一个类型为Collection的变量来引用。这就意味着任何列 表或集构成的集合都可以用这种方式引用,只有映射类除外(但也不是完全排除在外,因为可以从映射获得一个列表。)所以说,把一个列表或集传递给方法的标准 途径是使用Collection类型的参数。

Vector 还是ArrayList,哪一个更好,为什么?

要回答这个问题不能一概而论,有时候使用Vector比较好;有时是ArrayList,有时候这两个都不是最好的选择。你别指望能够获得一个简单肯定答案,因为这要看你用它们干什么。下面有4个要考虑的因素:

(1)API

(2)同步处理

(3)数据增长性

(4)使用模式

下面针对这4个方面进行一一探讨

API
在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这样的描述,Vector类似于ArrayList.。所有从API的角度来看这两个类非常相似。但他们之间也还是有一些主要的区别 的。

同步性

Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的 对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于 同步带来的不必要的性能开销。

数据增长

从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候,如果元素 的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%, 所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势,因为你可以通过设置集 合的初始化大小来避免不必要的资源开销。

使用模式

在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的,这个时间 我们用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加 或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢?

这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好选 择其他的集合操作类。比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的使用缺比较 慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList 也会为每个插入的元素创建对象,所有你要明白它也会带来额外的开销。

最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组 (Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。

Java集合类

总的说来,Java API中所用的集合类,都是实现了Collection接口,他的一个类继承结构如下:
Collection<--List<--Vector
Collection<--List<--ArrayList
Collection<--List<--LinkedList
Collection<--Set<--HashSet
Collection<--Set<--HashSet<--LinkedHashSet
Collection<--Set<--SortedSet<--TreeSet

首先看下面这张表,本文即通过它展开相关内容。

 

 

Implementations

Hash Table

Resizable Array

Balanced Tree

Linked List

Hash Table + LinkedList

Interfaces

Set

HashSet

TreeSet

LinkedHashSet

List

ArrayList

LinkedList

Map

HashMap

TreeMap

LinkedHashMap

图1

 

Java集合类的种类

注意图1第二列,Java在设计集合结构时,把集合划成3类:第一种Set,普通的无序集;第 二种List,偏序集;第三种Map,有序对的集合。其中Set和List都继承自Collection接口,Map没有从Collection继承。这 样做也是没办法的事,因为Map并不是一个通常意义的集合,它的元素是一个key-value-pair,而Collection根本就不是Map,它还 没有key的概念。虽然Collection和Map彼此没有继承关系,但它们有大量同名的方法。值得注意的是,Map提供了keySet()和 values()两个方法,分别返回一个key的集合和值的集合。

示例1:通过对一个HashSet对象和一个ArrayList对象中元素使用跌代器可以显示Set与List中元素之间的顺序关系:Set中元素是无序的,List不是。

 

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class ElementOrder ...{
    public static void main(String[] args) ...{
        Iterator it = null;
        Set ht = new HashSet();
        ht.add("htA");
        ht.add("htB");
        ht.add("htC");
        ht.add("htD");
        it = ht.iterator();
        
        while (it.hasNext()) ...{
            System.out.println(it.next());
        }
        
        List al = new ArrayList();
        al.add("alA");
        al.add("alB");
        al.add("alC");
        al.add("adD");
        it = al.iterator();
        
        while (it.hasNext()) ...{
            System.out.println(it.next());
        }
    }
}

 

示例2:Map类型的集合对象通过keySet()和values()两个方法,分别返回一个key的集合(Set类型)和值的集合(Collection类型)

 

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class TestMap ...{
    public static void main(String[] args) ...{
        Iterator it = null;

        Map tm = new HashMap();
        tm.put("a", new Integer(1));
        tm.put("b", new Integer(2));
        tm.put("c", new Integer(3));

        Set tk = tm.keySet();
        it = tk.iterator();

        while (it.hasNext()) ...{
            System.out.println(it.next());
        }

        Collection tv = tm.values();
        it = tv.iterator();

        while (it.hasNext()) ...{
            System.out.println(it.next());
        }
    }
}

 

Java集合类基于的常见数据结构

注意到图1 的第二行,是Java集合类基于的常见数据结构,它是集合对象中元素的组织方式。不同数据结构的类都有一些特定操作,参见示例。

示例3 List的一些排序操作。来自TheJava Developers Almanac 1.4

 

    // Create a list 
    String[] strArray = new String[] ...{"z", "a", "C"}; 
    List list = Arrays.asList(strArray); 
    // Sort 
    Collections.sort(list); // C, a, z 
    // Case-insensitive sort
    Collections.sort(list, String.CASE_INSENSITIVE_ORDER); // a, C, z 
    // Reverse-order sort 
    Collections.sort(list, Collections.reverseOrder()); // z, a, C 
    // Case-insensitive reverse-order sort 
    Collections.sort(list, String.CASE_INSENSITIVE_ORDER); 
    Collections.reverse(list); // z, C, a

 

区别与联系

图1种各类的区别和联系很明显:同行同集合类性,同列同数据结构。

操作

根据Java集合框架的体系,不同的集合类在拥有父类型的操作时由于本身的集合类型和数据结构类型的不同,都有其特有的方法。兄弟类之间有同名的方法也可能因为同样的原因有不同的实现,这正好体现了OO的多态性。

其他

不可忽视的是java.util.Collections提供很多操作集合对象的静态方法。集合在Java还有其他非官方实现形式,如开源的Trove等。值得一提的是,示例1中Set ht = new HashSet();的定义方式体现了DIP(Dependence InversionPrinciple,依赖倒转原则)。


  集合中存放的是对象的引用,而非对象本身 ,出于表达上的便利,简称为"集合中的对象".
  Set(集):集合中的对象不按特定方式排列,并且没有重复对象,它的有些实现类能对集合中的对象按特定方式排列.
     set接口主要有两个实现类HashSet和TreeSet,HashSet类按照哈希算法来存取集合中的对象,存取速度比较快,HashSet类还有一个子类LinkedHashSet类,不仅
     实现了哈希算法,而且实现了链表数据结构,TreeSet类实现了SortedSet接口,具有排序功能.
     那么,当一个新的对象加入到Set集合中,Set的add()方法是如何判断这个对象是否已经存在于集合中的呢?
      boolean isExists=false;
      Iterator it=set.iterator();
      while(it.hasNext())
      {
           ObjectoldObject=it.next();
          if(newObject.equals(oldObject))
             {
                 isExists=true;
                 break;
             }
      }
      可见,Set采用对象的equals()方法比较两个对象是否相等,而不是采用"=="比较运算符,以下程序代码尽管两次调用了Set的add()方法,
      实际上只加入了一个对象:
      Set set=new HashSet();
      String s1=new String("hello");
      String s2=new String("hello");
       set.add(s1);
       set.add(s2);
      虽然变量s1和s2实际上引用的是两个内存地址不同的字符串对象,但是由于s2.equals(s1)的比较结果为true,因此Set认为他们是相等的对象,当第二次调用
      Set的add()方法时,add()方法不会把s2引用的字符串对象加入到集合中.
   HashSet类
    按照哈希算法来存取集合中的对象,具有很好的存取性能,当HashSet向集合中加入一个对象时,会调用对象的hashCode()方法获得哈希码,然后根据这个哈希码
    进一步计算出对象在集合中的存放位置.
    在Object类中定义了hashCode()和equals()方法,Object类的euqals()方法按照内存地址比较对象是否相等,因此如果object1.equals(object2)为true,

表明object1
    变量和object2变量十九上引用同一个对象.那么object1和object2的哈希码也应该相同.
    ***如果用户定义的类覆盖了Object类的equals()方法,但是没有覆盖Object类的hashCode()方法,就会导致当object1.equals(object2)为true时,而object1和object2的
       哈希码不一定一样,这样使HashSet无法正常工作.
   TreeSet类:
       实现了SortedSet接口,能够对集合中的对象进行排序.
       如:
         Set set=new TreeSet();
          set.add(new Integer(7));
          set.add(new Integer(9));
          set.add(new Integer(8));
         Iterator it=set.iterator();
          while(it.hasNext())
          {
              System.out.println(it.next());
          }
         输出结果为:6 7 8
      当TreeSet向集合中加入一个对象时,会把它插入到有序的对象序列中,那么TreeSet是如何对对象进行排序的捏?TreeSet支持
      两种排序方式:自然排序和客户化排序,默认情况下是自然排序.
      在JDK中,有一部分类实现了Comparable接口,如Integer,Double和String等,Comparable接口有一个compareTo(Object o)方法,
      它返回整数类型,对于表达式x.compareTo(y),如果返回值为0,表示x和y相等,如果返回值大于0,表示x大于y,如果小于0,表示x<y.
       TreeSet调用对象的compareTo()方法比较集合中对象的大小,然后进行升序排序,这种方式称为自然排序.
   客户化排序:
       java.util.Comparator接口用于指定具体的排序方式,它有个compare(Objectobj1,Object obj2),用于比较两个对象的大小.
       当表达式compare(x,y)的值大于0,表示x大于y,小于0,表示x小于y,等于0,表示x等于y,如果想让TreeSet进按照Customer对象的
       name属性进行降序排列,可以先创建实现Comparator接口的类CustomerComparator,如:
       import java.util.*;
       public class CustomerComparator implementsComparator

{
             public int compare(Object o1,Object o2)
             {
                    Customer c1=(Custoemr)o1;
                    Customer c2=(Customer)o2;
                   if(c1.getName().compareTo(c2.getName())>0) return -1;
                   if(c1.getName().compareTo(c2.getName())<0) return 1;
                   return 0;
             }
        }
        接下来在构造TreeSet的实例时,调用它的TreeSet(Comparator comparator)构造方法
             Setset=new TreeSet(new CustomerComparator());
              Customer c1=new Customer("TOM",15);
              Customer c2=new Customer("JACK",20);
              Customer c3=new Customer("MIKE",38);
              set.add(c1);set.add(c2);set.add(c3);
             Iterator it=set.iterator();
              while(it.hasNext())
              {Custoemr customer=(Customer)it.next();
   &n

bsp;             System.out.println(customer.getName()+"" +customer.getAge();)
              }
         当TreeSet向集合中加入Customer对象时,会调用CustomerComparator类的compare()方法进行排序,以上Tree按照
         Custoemr对象的name属性进行降序排列,最后输出为:
         TOM 15    MIKE38 JACK 16
  
  List(列表):对象以线性方式存储,集合中的对象按索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象.
          实现类有LinkedList,ArrayList和Vector,LinkedList采用链表数据结构,而ArrayList代表大小可变的数组,Vector和
          ArrayList比较相似,两者的区别在于Vecotr类的实现采用了同步机制,而ArrayList没有使用同步机制/
          List按索引排列:
             List list=new ArrayList();
             list.add(new Integer(3));
             list.add(new Integer(4));
             list.add(new Integer(3));
             list.add(new Integer(2));
             List的get(int index)方法返回集合中由参数index指定的索引位置的对象,第一个加入到集合中的对象的索引位置为0,
             for( int i=0,i<list.size;i++)
               {System.out.println(list.get(i));}
              输出结果为:3 4 3 2.
          List只能对集合中的对象按索引位置排序,如果希望对List中的对象按其他特定方式排序,可以借助Comparator接口和Collections类.
          Collections类是Java集合API中的辅助类,它提供了操纵集合的各种静态方法,

其中sort()方法用于对List中的对象进行排序:
          sort(List list):对List中的对象进行自然排序.
          sort(Listlist,Comparator comparator):对List中的对象进行客户化排序,comparator参数指定排序方式.
          对以下List进行自然排序:
          List list=newArrayList();
          list.add(newInteger(3));
          list.add(newInteger(4));
          list.add(newInteger(3));
          list.add(newInteger(2));
          Collections.sort(list);
          for(inti=0;i<list.size();i++)
          {
                System.out.println(list.get(i));
          }
          以上输出结果:2 3 3 4
  Map(映射):集合中的每一个元素包含一对键对象和一对值对象,集合中没有重复的键对象,值对象可以重复,它的有写实现类能
  对集合中的键对象进行排序.
  Map map=new HashMap();
  map.put("1","Mon");
  map.put("1",Monday);
  map.put("2","monday");
  由于第一次和第二次加入到Map中的键对象都是1,所以第一次加入的值对象将被覆盖,而第二个和第三个的值对象虽然相同,但是
  键对象不一样,所以分配了不同的地址空间,所以不会覆盖,也就是说一共有两个元素在Map集合中.
 
  Map有两种比较常用的实现:HashMap和TreeMap.Hashmap按照哈希算法来存取键对象,有很好的存取能力,为了保证HashMap能正常工作,
  和HashSet一样,要求当两个键对象通过equals()方法比较为true时,这两个键对象的hashCode()方法返回的哈希码也一样.
  TreeMap实现了SortedMap接口,能对键对象进行排序,和TreeSet一样,TreeMap也支持自然排序和客户化排序两种方式,以下程序中的TreeMap
  会对四个字符串类型的键对象"1","3","4","2"进行自然排序:
  Map map=new TreeMap();
  map.put("1","Monday");
  map.put("3","Wendsday");
  map.put("4","Thursday");
  map.put("2",

"Tuesday");
  //返回集合中所有键对象的集合
  Set keys=map.keySet();
  Iterator it=keys.iterator();
  while(it.hasNext)
  {String key=(String)it.next();
   //根据键对象得到值对象
   String value=(String)map.get(key);
   System.out.println(key+"" +value);
  }
  以上输出结果为:1 Monday  2 Wendsday 3 Thursday 4Tuesday
 
  如果希望TreeMap进行客户化排序,可以调用它的另一个构造方法TreeMap(Comparatorcomparator),参数comparator指定具体的排序方式.

猜你喜欢

转载自himo-zhang.iteye.com/blog/2288722