第十五章、Java集合


为了使程序能方使地存储和操纵数门不固定的一组数据,JDK 类库提供了Java集合,所有 Java 集合类都位于 java.util 包中。与 Java 数组不同,Java集合中不能存放基本类型数据,而只能存放对象的引用。Java 集合主要分为4 种类型:

  • Set(集):集合中的对象不按特定方式排序,并且没有重复的对象。它的有些实现类能对集合中的对象按特定方式排序。
  • List(列表):集合中的对象按照索引位置排序,可以有重复的对象,允许按照对象在集合中的索引位置检索对象。List 与数组有些相似。
  • Queue(队列):集合中的对象按照先进先出的规则来排列。在队列的未尾添加元素,在队列的头部删除元素。可以有重复的对象。双向队列则允许在队列的末尾和头部添加和删除元素。
  • Map(映射):集合中的每一个元素包含对键(Key)对象和值(Value)对象,集合中没们重复的键对象,值对象可以重复。它的有些实现类能对集合中的键对象进行排序。

15.1 Collection 和 Iterator 接口

Collection 接口中声明了适用于Java集合(只包括 Set、List 和 Queue) 的通用方法。

方法描述
boolean add(Object o)向某合中加入-个对象的引用
void clear()删除集合中的所有对象,即不再持有这些对象的引用
boolean contains(Object o)判断在集合中是否持特定对象的引用
boolean isEmpty()判断集合是否为空
Iterator iterator()返回一个 Iterator 对象 ,可用它来遍历集合中的元素
boolean remove(Object o)从集合中删除一个对象的引用
int size()返回集合中元素的数目
Object[] toArray()
<T> T[] toArray(T[] a)
返回一个数组,该数组包含集合中的所有元素

iterator 接口隐藏底层集合的数据结构,向客户程序提供了遍历各种类型的集合的统一接口。Iterator 接口中声明了如下方法:

方法描述
boolean hasNext()判断集合中的元素是否遍历完毕,如果没有,就返回 true
E next()返回下一个元素
void remove()从集合中删除由 next()方法返同的当前元素
import java.util.*;
public class Visitor {
    
    //如果集合没有排序,Iterator 遍历集合中元素的顺序是任意的,并不一定与加入元素的顺序一致
    public static void print(Collection<? extends Object> c) {
        Iterator<? extends Object> it = c.iterator();
        while (it.hasNext()) {
            Object element = it.next();
            System.out.println(element);
        }
    }

    //只有实现了 java.lang.Iterable 接口的对象才允许使用 foreach 语句进行遍历
    public static void printWithForEach(Collection<? extends Object> c) {
        for (Object element : c) 
            System.out.println(element);
    }

    public static void main(String[] args) {
        Set<String> set = new HashSet<String>();
        set.add("Tom");
        print(set);

        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("Tom", 23);
        printWithForEach(map.entrySet());
        //map.entrySet()方法返回一个存放了Map.Entry元素集合,每个Map.Entry元素表示一对键值
    }
}

当通过 Collection 集合的 Iterator() 方法得到一个 Iterator 对象后 , 如果当前线程或其他线程接着又通过 Collection 集合的一些方法对集合进行了修改操作(调用当前 Iterator 对象的 remove() 方法来修改集合除外),接下来访问这个 Iterator 对象的 next() 方法会导致 Java.util.ConcurrentModificationException 运行时异常

import java.util.*;
public class Concurren Tester{
    public static void main(String[] args) {
        final int size = 1000;
        final Set<Integer> set = new HashSet<Integer>();
        for (int i = 0; i < size; i++) 
            set.add(new Integer(i));
        
        Thread reader = new Thread() {
            public void run() {
                for (Integer i : set) {
                    System.out.println(i);          //抛出ConcurrentModificationException
                    yield();                        //把CPU让出给别的线程
                }
            }
        }
        Thread remover = new Thread() {
            public void run() {
                for (int i = 0; i < size; i++) {
                    set.remove(new Integer(i));     //删除集合中元素
                    yield();                        //让出CPU
                }
            }
        }

        reader.start();
        remover.start();
    }
}

Iterator 对象运用了快速失败机制(fail-fast),一旦监测到集合已被修改(有可能是被其他线程修改的),就抛出 ConcurrentModificationException 运行时异常,而不是显示修改后的集合的当前内容,这可以避免潜在的由于共享资源竞争而导致的并发问题。

15.3 Set集

Set 是最简单的一种集合,集合中的对象不按特定方式排序,并且没有重复对象。Set 接口主要有两个实现类:HashSet 和 TreeSet。HashSet 类按照哈希算法来存取集合中的对象,存取速度比较快。HashSet类还有一个子类 LinkedHashSet 类,它不仅实现了哈希算法,而且实现了链表数据结构,链表数据结构能提高插入和删除元素的性能。TreeSet 类实现了 SortedSet 接口,具有排序功能。

HashSet 类按照哈希算法来存取集合中的对象,具有很好的存取和查找性能。当向集合中加入一个对象时,HashSet 会调用对象的 hashCode() 方法获得哈希码,然后根据这个哈希码进一步计算出对象在集合中的存放位置。在 Object 类中定义了 hashCode() 和 equals() 方法,Object 类的 equals() 方法按照内存地址比较对象是否相等,因此如果 object1.equals(object2) 为 true,表明 object1 变量和 object2 变量实际上引用同一个对象,那么 objectl 和 object2 的哈希码也肯定相同。所以如果用户定义的类覆盖了 Object 类 equals() 方法,就必须同时覆盖 Object 类的 hashCode() 方法并且保证两个相等的对象哈希码也一样。

TreeSet 类实现了 SortedSet 接口,能够对集合中的对象进行排序。TreeSet 支待两种排序方式:自然排序和客制化排序,默认情况下 TreeSet 采用自然排序方式。

  1. 自然排序
    • 在 JDK 类库中,有一部分类实现了 Comparable 接口,如 Integer、Double 和 String等。Comparable 接口有一个 compareTo(Object o) 方法,它返回整数类型。对于表达式 x.compareTo(y), 如果返回值为 0, 表示 x=y;如果返回值大于0, 表示 x>y;如果返回值小于0,表示 x<y。
      Set<Object> set = new TreeSet<Object>();
      set.add(new Integer(8));
      set.add(new String("9"));           //抛出ClasCastException
      
    • 如果用户自定义的类没有实现 Comparable 接口,会在第二次调用TreeSet的add()方法抛出ClasCastException 异常。避免此类异常需要实现该接口并实现 compareTo() 方法。
      public class Customer implements Comparable {
          private String name;
          private int age;
          ...
          public int compareTo(Object o) {
              Customer other = (Customer) o;
              if (this.name.compareTo(other.getName()) > 0) return 1;
              else if (this.name.compareTo(ohter.getName()) < 0) return -1;
              if (this.age > other.getAge()) return 1;
              else if (this.age < other.getAge()) return -1;
              return 0;
          }
          public boolean equals(Object o) {               //相等条件为name、age属性相等
              if (this == o) return true;
              if (!(o instanceof Customer)) return false;
              final Customer other = (Customer) o;
              
              if (this.name.equals(other.getName()) && this.age == other.getAge())
                  return true;
              else 
                  return false;
          } 
      
          public int hashCode() {              //重写hashCode,保证两对象相等其哈希码也相等
              int result;
              result = (name == null ? 0 : name.hashCode());
              result = 29 * result + age;
              return result;
          }
      }
      
  2. 客制化排序
    • 除了自然排序外,TreeSet 还支持客户化排序。java.util.Comparator<T> 接口提供具体的排序方式,<T> 指定被比较的对象的类型,Comparator 有 compare(T x, T y) 方法,用于比较两个对象的大小。当该方法的返回值等于0,表示 x=y;返回值大于0,表示 x>y;返回值小于0, 表示 x<y。
    • 如果希望 TreeSet 按照自定义对象的某个属性进行排序,可以先创建一个实现 Comparator 接口的类,然后构造TreeSet实例时,调用 TreeSet(Comparator comparator) 构造方法。
      import java.util.*;
      public class CustomerComparator implements Comparator<Customer> {
          public int compare(Customer c1, Customer c2) {
              if (c1.getName().compareTo(c2.getName()) > 0) return -1;
              else if (c1.getName().compareTo(c2.getName()) < 0) return 1;
              else return 0;
          }
      }
      public static void main(String[] args) {
          Set<Customer> set = new TreeSet<Customer>(new CustomerComparator());
          ...
      }
      

15.4 List列表

List 的主要特征是其元素以线性方式存储,集合中允许存放重复对象。List 接口主要的实现类包括:

  • ArrayList:长度可变的数组。允许对元素进行快速的随机访问,但插入与删除元素的速度较慢。
  • LinkedList:在实现中采用链表数据结构。对顺序访问进行了优化,插入和删除元素的速度较快,随机访问则相对较慢。单独具有 addfirst()、addLast()、getFirst()、getLast()、removeFirst()和removeLast()方法,这些方法使得它可以作为堆栈、队列和双向队列使用。

访问列表元素

List 中的对象按照索引位置排序,客户桯序可以按照对象在集合中的索引位置来检索对象。List 的 get(int index) 方法返回集合中由参数 index 指定不同索引位置的对象。List 的 iterator() 能返回 Iterator 对象,可以用 Iterator 来遍历集合中的所有对象。

for (int i = 0; i < list.size(); i++) System.out.println(list.get(i));

Iterator<Integer> it = list.iterator();
while (it.hasNext()) System.out.println(it.next());

for (Integer i : list) System.out.println(i);

列表排序

List 只能对集合中的对象按索引位置排序,如果希望对 List 中 的对象按其他特定的方式排序,可以借助 Comparator 接口和 Collections 类 。 Collections 类是 Java 集合类库中的辅助类,它提供了操纵集合的各种静态方法,其中 sort() 方法用于对 List 中的对象进行排序:

  • sort(List list):对 List 中的对象进行自然排序。
  • sort(List list, Comparator comparator):对 List 中的对象进行客户化排序,comparator 参数指定排序方式。

ListIterator接口

List 的 lisIterator() 方法返回一个 ListIterator 对象,ListIterator 接口继承了 Iterator 接 口, 此外还提供了专门操纵列表的方法:

  • add():向列表中插入一个元素。
  • hasNext():判断列表中是否还有下一个元素。
  • hasPrevious():判断列表中是否还有上一个元素。
  • next():返回列表中的下一个元素。
  • previous():返回列表中的上一个元素。
//向一个排序的 List 列表中按顺序插入数据。
public static void myInsert(List<Integer> list, int data) {
    ListIterator<Integer> it = list.listIterator();
    while (it.hasNext()) {
        Integer in = it.next();
        if (data <= in.intValue) {
            it.previous();
            it.add(data);
            break;
        }
    }
}

获得固定长度的 List 对象

java.util.Arrays 类的 asList() 方法能够把一个 Java 数组包装为一个 List 对象,这个 List 对象代表固定长度的数组。所有对 List 对象的操作都会被作用到底层的 Java 数组。由于数组的长度不能改变,因此不能调用这种 List 对象的 add()和 remove()方法,否则会抛出 java.lang.UnsupportedOperationException 运行时异常:

String[] ss = {"Tom", "Mike", "Jack"};
List<String> list = Arrays.asList(ss);          //数组转化为固定长度的List
list.set(0, "Jane");                            //合法,修改某个位置的元素
System.out.println(Arrays.toString(ss));        //打印

list.reomve("Mike");                            //抛出异常
list.add("Mary");                               //抛出异常

15.5 Queue队列

从 JDK5 开始,用 java.util.Queue 接口来表示队列。队列的特点是向末尾添加元素,从队列头部删除元素,队列中允许有重复元素。

  • 加入元素方法:
    boolean add(E element)
    boolean offer(E element)
    //以上两个方法都向队列的末尾添加元素,如果操作成功就返回 true 。参数的类型 E 为泛型类型。
    //这两个方法的区别在于,如果队列已满
    //add() 方法会抛出 IllegalStateException,而 offer() 方法返回 false。
    
  • 删除元素方法:
    E remove()
    E poll()
    //以上两个方法都会删除队列头部的元素。 这两个方法的区别在于,
    //如果队列为空,remove() 方法抛出NoSuchElementException,而 poll() 方法则返回 null。
    
  • 获取元素方法:
    E element()
    E peek()
    //以上两个方法都会返回队列头部的元素,但不删除它。这两个方法的区别在于,
    //如果队列为空,element() 方法抛出 NoSuchElementException,而 peek() 方法则返回 null。
    

Deque双向队列

Queue 接口是单向队列,它有一个子接口 Deque,表示双向队列。双向队列的特点是在队列的头部和末尾都可以添加或删除元素。

  • Deque 接口具有以下向队列头部或末尾添加元素的方法:

    void addFirst(E element) 
    void addLast(E element)
    boolean offerFirst(E element)
    boolean offerLast(E element)
    //如果队列已满,前两个方法抛出 IllegalStateException,而后两个方法则返回 false
    
  • Deque 接口具有以下从队列头部或末尾删除元素的方法:

    E removeFirst()
    E reomveLast()
    E pollFirst()
    E pollLast()
    //如果队列为空,前两个方法抛出 NoSuchElementException,而后两个方法则返回null
    
  • Deque 接口具有以下从队列头部或末尾获取元素(不会删除该元素)的方法:

    E getFirst()
    E getLast()
    E peekFirst()
    E peekLast()
    //如果队列为空,前两个方法抛出 NoSuchElementException,而后两个方法则返回 null
    

LinkedList 类和 ArrayDeque 类都实现了 Deque 接口。

PriorityQueue优先队列

PriorityQueue(优先级队列)会按照排序的方式对队列中的元素进行排序和检索。因此加入到 PriorityQueue 中的对象必须实现 Comparable 接口,提供对元素排序时两个元素之间的比较规则。

值得注意的是,当通过 foreach 语句遍历优先级队列时,获得的元素并没有进行排序,而在通过 remove() 方法删除元素时,该方法总是会删除当前队列中的最小元素。

import java.util.*;
public class PriorityQueueTester {
    public static void main(String[] args) {
        Queue<Intger> queue = new PriorityQueue<Integer>();
        queue.add(3);queue.add(1);queue.add(2);
        for (Integer i : queue) System.out.println(i);
        while (!queue.isEmpty()) System.out.println(queue.remove());
    }
}
//打印结果: 1 3 2       1 2 3

15.6 Map映射

Map(映射)是种把键对象和值对象进行映射的集合,它的每一个元素都包含一对键对象和值对象,而们对象仍可以是 Map 类型,以此类推,这样就形成了多级映射。向 Map 集合中加入元素时,必须提供一对键对象和值对象,从 Map 集合中检索元素时,只要给出键对象,就会返回对应的值对象。

put(Object key, Object value)               //向集合中加入元素
get(Object key)                             //检索与键对应的值对象

Map 集合中的键对象不允许重复,也就是说,任意两个键对象通过 equals() 方法比较的结果都是 false。对于值对象则没有唯一性的要求,可以将任意多个键对象映射到同一个值对象上。

Map 的 entrySet() 方法返回一个 Set 集合,在这个集合中存放了 Map.Entry 类型的元素,每个 Map.Entry 对象代表 Map 中的一个键值对。Map.Entry 对象的 getKey() 方法返回键,getValue() 方法返回值。

Map 有两种比较常用的实现:HashMap 和 Treemap。

  • HashMap 按照哈希算法来存取键对象,有很好的存取性能,为了保证 HashMap 能正常工作,和 HashSet 一样,要求当两个键对象通过 equals() 方法比较为 true 时,这两个键对象的 hashCode() 方法返回的哈希码也一样。
  • TreeMap 实现了 SortedMap 接口,能对键对象进行排序。和 TreeSet 一样,TreeMap 也支持自然排序和客户化排序两种方式。如果希望 TreeMap 对键对象进行客户化排序,可调用它的另一个构造方法 TreeMap(Comparator comparator),参数 comparator 指定具体的排序方式。

15.7 HashSet 和 HashMap 的负载因子

HashSet 和 HashMap 都运用哈希算法来存取元素。哈希表中的每个位置也称为 (bucket),在发生哈希冲突的时候,在桶中以链表的形式存放多个元素。

HashSet 和 HashMap 都有以下属性:

  • 容量 (capacity):哈希表中桶的数量。
  • 初始容量(initial capacity):创建 HashSet 和 HashMap 对象时桶的数量。在 HashSet 和 HashMap 的构造方法中允许设置初始容量。
  • 大小 (size):元素的数目。
  • 负载因子(load factor):等于 size/capacity。负载因子为0,表示空的哈希表;负载因子为 0.5, 表示半满的哈希表,以此类推。轻负载的哈希表具有冲突少、适合插入和查找的优点(但是用 Iterator 遍历元素的速度较慢)。HashSet 和 HashMap 的构造方法允许指定负载因子,当哈希表的当前负载达到用户设定的负载因子时,HashSet 和 HashMap 会自动成倍地增加容量(即桶的数量),并且重新分配原有的元素的位置。一般默认负载因子为 0.75。

15.8 Collections 集合实用类

之前介绍了 java.util.Arrays 类,它提供了一系列操纵 Java 数组的静态方法。对于 Java 集合,也有 一 个实用类:java.util.Collections,它的一部分静态方法专门用于操纵 List 类型集合,还有一部分静态方法可用于操纵所有的 Collection 类型或 Map 类型集合。

List 代表长度可变的数组, Collections 的以下方法适用于 List:

copy(List dest, List src)
//把一个 List 中的元素复制到另一个 List 中

fill(List list, Object o)
//向列表中填充元素

sort(List list)
//把 List 中的元素进行自然排序

binarySearch(List list, Object key)
//查找 List 中与给定对象 key 相同的元素。
//在调用该方法时,必须保证 List 中的元素已经自然排序,这样才能得到正确的结果。

binarySearch(List list, Object key, Comparator c)
//查找 List 中与给定对象 key相同的元素,Comparator 类型的参数指定比较规则
//在调用该方法时,必须保证 List 中的元素已经按照 Comparator 类型的参数的比较规则排序

shuffle(List list)
//对 List 中的元素进行随机排列

Collections 的以下方法适用于 Collection 类型或 Map 类型:

Object max(Collection coll)
Object min(Collection coll)
//返回集合中的最大/小元素,采用自然排序的比较规则

Object max(Collection coll, Comparator comp)
Object min(Collection coll, Comparator comp)
//返回集合中的最大/小元素,Comparator 类型的参数指定比较规则

Set singleton(Object obj)
//返回一个不可改变的 Set 集合,它只包含一个参数指定的对象 

List singletonList(Object obj)
//返回一个不可改变的 List 集合,它只包含一个参数指定的对象

Map singletMap(Object key, Object value)
//返回一个不可改变的 Map 集合,它只包含参数指定的一对键与值

Collection synchronizedCollection(Collection c)
List synchronizedList(List list)
Map synchronizedMap(Map map)
Set synchronizedSet(Set set)
//在原来集合的基础上,返回支持同步的(即线程安全的)集合。

Collection unmodifiableCollection(Collection c)
List unmodifiableList(List list)
Map unmodifiableMap(Map map)
Set unmodifiableSet(Set set)
//在原来集合的基础上,返回不允许修改的集合视图 。

如果集合中仅仅包含一个元素,并且不允许修改这个集合,那么可以用 Collections 的 singletonXXX() 方法来构造这样的集合。程序不允许对这个集合进行添加或删除操作,否则会导 java.lang.UnsupportedOperationException 运行时异常。

如果集合中的元素不允许修改,可以用 Collections 的 unmodifiableXXX() 方法来获得原始集合的一个集合视图。桯序可以读取集合视图中的内容,但不允许修改它。如果直接对原始集合做了修改,集合视图会反映修改后的集合的内容:

Set<String> originalSet = new HashSet<String>();
originalSet.add("Tom"); originalSet.add("Mike");

Set<String> setView = Collections.unmodifiableSet(originalSet);
originalSet.add("Linda");
System.out.println(Arrays.toString(setView.toArray())); //打印 Tome Mike Linda
setVie.add("Mary");                                     //抛出UnsupportedOperationException异常

15.9 线程安全的集合

在 Java 集合框架中, Set、List、Queue 和 Map 的实现类都没有采取同步机制。在单线程环境中,这种实现方式会提高操纵集合的效率,Java 虚拟机不必因为管理同步锁而产生额外的开销。但在多线程环境中,可能会有多个线程同时操纵同一个集合,为了避免并发问题,可以采取以下几种解决措施:

  • 在程序中对可能导致并发问题的代码块进行同步。
  • 利用 Collections 的 synchronizedXXX() 方认获得原始集合的同步版本:
    Collection synchronizedCollection = Collections.sychronizedCollection(originalCollection);
    
  • 如果集合只包含单个元素并且不允许被修改,可以用 Collections 的 singletonXXX() 方法来构造这样的集合,这可以避免集合被线程错误地修改,而且由于不必采取同步措施,可以提高并发性能。
  • 如果集合的元素不允许被修改,可以用 Collections 的 unmodifiableXXX() 方法来生成原始的集合视图,让线程只访问这个集合视图 ,这可以避免集合被线程错误地修改,而且由千不必采取同步措施,可以提高并发性能。
  • 用 Collections 的 synchronizedXXX() 方法获得原始集合的同步版本后,如果一个线程操纵集合的同步版本,而另一个线程操纵原始的集合,那么仍然会导致并发问题。为了避免这种悄况,可以直接采用 java.util.concurrent 并发包提供的线程安全的集合,例如: ConcurrentHashMap ConcurrentSkipListMap ConcurrentSkipListSet ConcurrentLinkedQueue。这些集合的底层实现采用了复杂的算法,保证多线程访 问集合时,既能保证线程之间的同步,又具有高效的并发性能。

15.10 集合与数组的互换

集合和数组都用来存放多个元素,它们之间可以通过特定的方式互相转换。

  • 把数组转换为集合。
    java.util.Arrays 类是一个数组实用类,它的 asList() 静态方法能够把数组转换成一个 List 对象
    Integer[] array = {1,2,3};
    List<Intger> list = Array.asList(array);
    
    大多数集合都有以下形式的构造方法,该构造方法在创建新集合的时候,会把参数 c 指定的集合中的元素复制到新集合中:
    HashSet(Collection<? extends E> c)
    TreeSet(Collection<? extends E> c)
    ArrayList(Collection<? extends E> c)
    LinkedList(Collection<? extends E> c)
    
    因此,在通过 Arrays.asList() 方法得到了 一个 List 对象后,还可以把它转换为其他类型的集合。
    Integer[] array = {1,2,3};
    List<Integer> list = Arrays.asList(array);
    List<Integer> arrayList = new ArrayList<Integer>(list);
    Set<Integer> hashSet = new HashSet<Integer>(list);
    
  • 把集合转换为数组
    java.util.Collection 接口中定义了 toArray() 方法,能把集合转换为数组,它有两种重载形式:
    • Object[] toArray() 返回 Object[] 类型数组。
    • <T> T[] toArray(T[] a) 返回泛型标记 <T> 指定类型数组。
    List<Integer> list = new ArrayList<Integer>();
    list.add(1); list.add(2); list.add(3);
    Object[] arr1 = list.toArray();                     //返回Object[]类型数组
    Integer[] arr2 = list.toArray(new Integer[0]);      //返回Integer[]类型数组
    //参数 new lnteger[0] 仅仅用来指定返回数组的类型
    

15.11 集合的批量操作

如果需要一次处理大批量数据,可以调用集合的支持批量操作的方法。在 Collection 接口中定义了以下方法:

boolean retainAll(Collection<?> c)
//修改当前集合,在当前集合中保留那些同时位于参数 c 集合中的元索,删除其余元素。(求交集)
//如果当前集合最终做了改动,就返回 true 。

boolean removeAll(Collection<?> c)
//删除当前集合中的那些同时位于参数 c 集合中的元素(去交集)

boolean addAll(Collection<? extends E> c)
//把参数 c 集合中的元素加入到当前集合中 

boolean containsAll(Collection<?> c)
//判断当前集合中是否存在参数 c 集合中的元素 

List<E> subList(int fromIndex, int toIndex)
//在 List 接口中一个用于获得子列表视图的方法(类似substring)
//fromIndex 参数和 toIndex 参数分别指定元素的起始索引和结束索引
//起始索引对应的元素会加入到子列表中,而结束索引对应的元素不会加入到子列表中。

15.12 历史集合类

在早期的 JDK1.0 版本中,代表集合的类只有 Vector Stack Enumeration Hashtable PropertiesBitSet 类。直到 JDK1.2 版本开始,才出现了 Collection Set ListMap 接口,以及各种实现类,它们构成了比较完整的 Java 集合框架, 在 JDK5 版本中,又增加了 Queue 接口及各种实现类。JDK1.0 版本中的集合类也称为历史集合类。

历史集合类描述缺点新集合框架中的替代
Vector集合中的元素有索引位置,在新的集合框架中改为实现了List接口采用了同步机制,影响操纵集合的性能ArrayList LinkedList
Stack堆栈,支持后进先出的操作采用了同步机制,影响操纵集合的性能;Stack继承了Vector类,使得Stack不能作为严格的堆栈,因为其支持随机访问LinkedList
Hashtable集合中的每个元素包含一个键值对。在新框架内改为实现了Map接口。采用了同步机制,影响操纵集合的性能HashMap
Properties集合中的每个元素包含一个键值对。继承了Hashtable采用了同步机制,影响操纵集合的性能
Enumeration用于遍历集合中的元素只能与Vector和Hashtable等历史集合配套Iterator
BitSet存放一组 boolean 类型数据,支持与、或、异或等操作

15.13 枚举类型

为了提高代码的可重用性,从 JDK5 开始,提供了抽象的 Java.lang.Enum 枚举类,它的声明如下:

public abstract class Enum<E extends Enum<E>>

Enum类是抽象类,<E extends Enum<E>> 为泛型标记,表示 Enum 类拥有的静态常量实例也是 Enum 类型或者其子类型。用户自定义的枚举类只需继承 Enum 类就行了。例如 Gender 枚举类可以继承 Enum 类:

public class Gender extends Enum {
    public static final Gender FEMALE;
    public static final Gender MALE;
    ...
}

//为了进一步简化编程,JDK5 提供了专门用千声明枚举类型的关键字 enum , 以上程序代码等价于:
public enum Gender{FEMALE, MALE}

Enum 类具有以下非抽象的方法:

int compareTo(E o)
//比较当前枚举常堂与指定对象的顺序

Class<E> getDeclaringClass()
//返回表示当前枚举常量的枚举类型的 Class 对象 

String name()
//返回当前枚举常量的名称

int ordinal()
//返回当前枚举常量 的序数,即它在枚举声明中的位置 ,其中初始枚举常量的序数为零

String toString()
//返回枚举常量的名称

static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
//根据指定的枚举类型和名称返回相应的枚举常量实例

static Enum[] value()
//以数组的形式返回该枚举类型的所有枚举常擞实例。

在自定义的枚举类中也可以定义构造方法和属性 。 这个构造方法必须是 private 类型的。

public class GenderNewTest {
    enum Gender{
        FELMAL("famle"),
        MALE("male");

        private String description;

        private Gender(String description) {
            this.description = description;
        }

        public String getDescription() {
            return description;
        }
    }
}

在 Java API 中,还为 Enum 类提供了两个适配器:

  • java.util.EnumSet 类:把枚举类型转换为集合类型。它的静态的 allOf() 方法能把枚举类的所有常量实例存放到一个 EnumSet 类型的集合中,然后返回这个集合。
  • Java.util.EnumMap 类:把枚举类型转换为映射类型。它的 EnumMap(Class<K> keyType) 构造方法用来指定具体的枚举类型。枚举常量以 Key 的形式存放在 Map 中。
import java.util.*;
public class ColorTester {
    enum Color{RED,BULE,YELLOW,GREEN}

    public static void main(String[] args) {
        /* EnumSet */
        EnumSet<Color> colorSet = EnumSet.allOf(Color.class);
        for (Color c : colorSet) System.out.println(c);

        /* EnumMap */
        EnumMap<Color, String> colorMap = new EnumMap<Color, String>(Color.class);
        ColorMap.put(Color.RED,"red");
        ...

        Set<Map.Entry<Color, String>> set = colorMap.entrySet();
        for (Map.Entry entry : set) System.out.println(entry.getKey() + " " + entry.getValue());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值