Java学习第六篇:集合类

一.Java集合类框架

Java集合大致可分为Set、List和Map三种体系,其中Set代表无序、不可重复的集合;List代表有序、重复的集合;而Map则代表具有映射关系的集合;从Java5以后,Java又增加了Queue体系集合,代表一种队列集合的实现。

Java的集合类主要由两个接口派生而来:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含一些子接口或实现类。Collection和Map接口、子接口及其实现类的继承树如下图所示。对于Set、List、Queue和Map四种集合,最常用的是HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList和HashMap、TreeMap等。

二.Collection和Iterator接口

1.Collection接口

Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于Set集合,也可以用于操作List和Queue集合。Collection接口里定义的方法见Java API文档。

import java.util.*;
public class CollectionTest {
    public static void main(String[] args) {
        Collection c=new ArrayList();
        //虽然集合里不能放基本类型的值,但Java支持自动装箱
        //关于自动装箱,见http://www.cnblogs.com/danne823/archive/2011/04/22/2025332.html
        //添加元素
        c.add("孙悟空");
        c.add(6);
        System.out.println("c集合的元素个数为:"+c.size());
        
        //删除指定元素
        c.remove(6);
        System.out.println("c集合的元素个数为:"+c.size());
        //判断是否包含指定字符串
        System.out.println("c集合是否包含\"孙悟空\"字符串:"+c.contains("孙悟空"));
        
        c.add("Java");
        System.out.println("c的集合元素:"+c);
        
        Collection books=new HashSet();
        books.add("Java");
        books.add("C++");
        System.out.println("c集合是否完全包含books集合?"+c.containsAll(books));
        
        //用c集合减去books集合里的元素
        c.removeAll(books);
        System.out.println("c的集合元素:"+c);
        
        //删除c集合里的所有元素
        c.clear();
        System.out.println("c的集合元素:"+c);
        
        System.out.println("books的集合元素:"+books);
        //books集合里只剩下c集合里也包含的元素
        books.retainAll(c);
        System.out.println("books的集合元素:"+books);
    }
}
View Code

运行结果:

c集合的元素个数为:2
c集合的元素个数为:1
c集合是否包含"孙悟空"字符串:true
c的集合元素:[孙悟空, Java]
c集合是否完全包含books集合?false
c的集合元素:[孙悟空]
c的集合元素:[]
books的集合元素:[C++, Java]
books的集合元素:[]

2.Iterator接口

Iterator则主要用于遍历Collection集合中的元素,Iterator对象称为迭代器。Iterator接口里定义了如下三个方法:

import java.util.*;
public class IteratorTest {
    public static void main(String[] args) {
        //创建集合
        Collection books=new HashSet();
        books.add("Java");
        books.add("C++");
        books.add("JavaScript");
        
        //获取books集合对应的迭代器
        Iterator iterator=books.iterator();
        while(iterator.hasNext()) {
            //iterator.next()方法返回的数据类型是Object类型
            //需要强制类型转换
            String book=(String)iterator.next();
            System.out.println(book);
            if(book.equals("Java")) {
                //从集合中删除上一次next方法返回的元素
                iterator.remove();
            }
        }
        
        //输出集合
        System.out.println(books);
    }
}
View Code

运行结果:

C++
JavaScript
Java
[C++, JavaScript]

注:当使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只能通过Iterator的remove方法删除上一次next方法返回的集合元素才可以;否则将会引发java.util.ConcurrentModificationException异常。

import java.util.*;
public class IteratorErrorTest {
    public static void main(String[] args) {
        //创建集合
        Collection books=new HashSet();
        books.add("Java");
        books.add("C++");
        books.add("JavaScript");
        
        //获取books集合对应的迭代器
        Iterator iterator=books.iterator();
        while(iterator.hasNext()) {
            String book=(String)iterator.next();
            System.out.println(book);
            if(book.equals("C++")) {
                //使用Iterator迭代过程中,不可以修改集合元素,只能通过迭代器修改
                //下面的代码引发异常
                books.remove(book);
            }
        }
    }
}
View Code

运行结果:

3.使用foreach循环变量集合元素

除了使用迭代器Iterator接口迭代访问Collection集合元素之外,使用foreach循环遍历更加便捷。

import java.util.*;
public class ForeachTest {
    public static void main(String[] args) {
        //创建集合
        Collection books=new HashSet();
        books.add("C++");
        books.add("Java");
        books.add("JavaScript");
        
        //foreach循环遍历
        for (Object obj : books) {
            String book=(String)obj;
            System.out.println(book);
        }
    }
}
View Code

运行结果:

C++
JavaScript
Java

注:当使用foreach循环迭代访问集合元素时,同样不能改变该集合,否则将引发ConcurrentModificationException异常。

三.Set集合

Set集合不允许包含相同的元素,Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回的是true,Set就不会接受这两个对象;反之,只要两个对象用equals方法比较返回false,Set就会接受这两个对象。

import java.util.*;
public class SetTest {
    public static void main(String[] args) {
        //创建集合
        Set books=new HashSet();
        //添加
        books.add(new String("Java"));
        //books集合两次添加的字符串对象明显不是同一个对象(因为两次都调用了new关键字创建字符串)
        //这两个字符串使用==运算符判断肯定返回false,但通过equals方法比较则返回true,所以添加失败
        boolean result=books.add(new String("Java"));
        //集合里只有一个元素
        System.out.println(result+"--->"+books);
    }
}
View Code

运行结果:

false--->[Java]

1.HashSet集合

(1).HashSet集合的特点

①不能保证元素的排列顺序,顺序有可能发生变化;

②HashSet不是同步的,如果多个线程同时访问HashSet,则必须通过代码保证其同步;

③集合元素可以为null.

(2).HashSet判断集合元素相同的标准

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的HashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。如果两个元素通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。

import java.util.*;
public class HashSetTest {
    public static void main(String[] args) {
        HashSet books=new HashSet();
        books.add(new A());
        books.add(new A());
        books.add(new B());
        books.add(new B());
        books.add(new C());
        books.add(new C());
        System.out.println(books);
    }
}

//类A重写了equals,但没有重写hashCode
class A {
    public boolean equals(Object obj) {
        return true;
    }
}

//类B重写了hashCode,但没有重写equals
class B {
    public int hashCode() {
        return 1;
    }
}

//类C重写了equals和hashCode
class C {
    public boolean equals(Object obj) {
        return true;
    }
    public int hashCode() {
        return 2;
    }
}
View Code

运行结果:

[B@1, B@1, C@2, A@1afae45, A@39443f]

2.LinkedHashSet集合

LinkedHashSet使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。LinkedHashSet需要维护元素的插入顺序,因此性能低于HashSet,但在迭代访问Set里的全部元素时将有很好的性能,因为它以链表来维护内部的顺序。

import java.util.*;
public class LinkedHashSetTest {
    public static void main(String[] args) {
        //创建集合
        LinkedHashSet books=new LinkedHashSet();
        books.add("Java");
        books.add("C++");
        System.out.println(books);
        
        books.remove("Java");
        books.add("Java");
        System.out.println(books);
    }
}
View Code

运行结果:

[Java, C++]
[C++, Java]

3.TreeSet集合

TreeSet可以确保集合元素处于排序状态,与HashSet集合采用hash算法来决定元素的存储位置不同,TreeSet采用红黑树的数据结构来存储集合元素。

import java.util.*;
public class TreeSetTest {
    public static void main(String[] args) {
        //创建集合
        TreeSet nums=new TreeSet();
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        //输出集合元素
        System.out.println(nums);
        //输出第一个元素
        System.out.println(nums.first());
        //输出最后一个元素
        System.out.println(nums.last());
        //返回小于4的子集,不包含4
        System.out.println(nums.headSet(4));
        //返回大于5的子集,如果set中包含5,子集中也包含5
        System.out.println(nums.tailSet(5));
        //返回大于等于-3、小于4的子集
        System.out.println(nums.subSet(-3, 4));
    }
}
View Code

运行结果:

[-9, 2, 5, 10]
-9
10
[-9, 2]
[5, 10]
[2]

TreeSet支持两种排序方法:自然排序和定制排序。在默认情况下,TreeSet采用自然排序。

(1).自然排序

TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排序,这种方式就是自然排序。当一个对象调用该方法与另一个对象进行比较时,例如obj1.compareTo(obj2),如果该方法返回0,则表明这两个对象相等;如果该方法返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表面obj1小于obj2.

如果向TreeSet添加的对象是程序员自定义类对象,则前提是用户自定义类实现了Comaprable接口;对于TreeSet集合而言,判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较返回0----如果返回0,则认为它们相等,否则认为它们不相等。

import java.util.*;
public class TreeSetTest2 {
    public static void main(String[] args) {
        TreeSet set=new TreeSet();
        Z z1=new Z(6);
        set.add(z1);
        //虽然是同一个对象,但依然添加成功,因为compareTo方法返回的不是0
        //所以自定义类重写compareTo和equals方法时,当equals方法返回true时,
        //compareTo方法应返回0
        System.out.println(set.add(z1));
        System.out.println(set);
        ((Z)(set.first())).age=9;
        System.out.println(((Z)(set.last())).age);
    }
}

class Z implements Comparable {
    int age;
    public Z(int age) {
        this.age=age;
    }
    
    //重写equals
    public boolean equals(Object obj) {
        return true;
    }
    
    //重写compareTo
    public int compareTo(Object obj) {
        return 1;
    }
}
View Code

运行结果:

true
[Z@55e55f, Z@55e55f]
9

import java.util.*;
public class TreeSetTest3 {
    public static void main(String[] args) {
        TreeSet ts=new TreeSet();
        ts.add(new R(5));
        ts.add(new R(-3));
        ts.add(new R(9));
        ts.add(new R(-2));
        //打印TreeSet
        System.out.println(ts);
        //取出第一个元素
        R first=(R)ts.first();
        //对第一个元素的count赋值
        first.count=20;
        //取出最后一个元素
        R last=(R)ts.last();
        //对最后一个元素的count赋值
        last.count=-2;        
        //打印TreeSet
        System.out.println(ts);
        //删除元素
        System.out.println(ts.remove(new R(-2)));
        System.out.println(ts);
        System.out.println(ts.remove(new R(5)));
        System.out.println(ts);
    }
}

class R implements Comparable {
    int count;
    public R(int count) {
        this.count=count;
    }
    
    //override
    public String toString() {
        return "R[count:"+count+"]";
    }
    
    //override
    public boolean equals(Object obj) {
        if(this==obj) {
            return true;
        }
        if(obj!=null && obj.getClass()==R.class) {
            R r=(R)obj;
            if(r.count==this.count) {
                return true;
            }
        }
        return false;
    }
    
    //Override
    public int compareTo(Object obj) {
        // TODO Auto-generated method stub
        R r=(R)obj;
        return this.count>r.count ? 1 :
                this.count<r.count ? -1 :0;
    }
}
View Code

运行结果:

[R[count:-3], R[count:-2], R[count:5], R[count:9]]
[R[count:20], R[count:-2], R[count:5], R[count:-2]]
false
[R[count:20], R[count:-2], R[count:5], R[count:-2]]
true
[R[count:20], R[count:-2], R[count:-2]]

(2).定制排序

如果要实现定制排序,例如降序排列,则可以通过Comparator接口帮助。该接口里包含一个int compare(T o1,T o2)方法,该方法用于比较o1和o2的大小,如果返回正整数,则o1大于o2;如果返回0,则o1等于o2;如果返回负整数,则o1小于o2.

import java.util.*;
public class TreeSetTest4 {
    public static void main(String[] args) {
        TreeSet ts=new TreeSet(new Comparator() {
            //定制排序
            //@Override
            public int compare(Object obj1, Object obj2) {
                M m1=(M)obj1;
                M m2=(M)obj2;
                return m1.age>m2.age? -1 :
                        m1.age<m2.age? 1 :0;
            }
        });
        ts.add(new M(5));
        ts.add(new M(-3));
        ts.add(new M(9));
        System.out.println(ts);
    }
}

class M {
    int age;
    
    public M(int age) {
        this.age=age;
    }
    
    public String toString() {
        return "M[age:"+age+"]";
    }
}
View Code

运行结果:

[M[age:9], M[age:5], M[age:-3]]

4.EnumSet集合

EnumSet是一个专门为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。

import java.util.*;
public class EnumSetTest {
    public static void main(String[] args) {
        //创建一个EnumSet集合,集合元素就是Season枚举类的全部枚举值
        EnumSet es1=EnumSet.allOf(Season.class);
        System.out.println(es1);
        
        //创建一个空的EnumSet集合,指定其集合元素是Season类的枚举值
        EnumSet es2=EnumSet.noneOf(Season.class);
        System.out.println(es2);
        //添加元素
        es2.add(Season.WINTER);
        es2.add(Season.SPRING);
        System.out.println(es2);
        
        //以指定枚举值创建EnumSet集合
        EnumSet es3=EnumSet.of(Season.SUMMER,Season.WINTER);
        System.out.println(es3);
        
        //以某范围枚举值创建EnumSet集合
        EnumSet es4=EnumSet.range(Season.SUMMER, Season.WINTER);
        System.out.println(es4);
        
        //es5+es4=Season枚举类的全部枚举值
        EnumSet es5=EnumSet.complementOf(es4);
        System.out.println(es5);
    }
}

enum Season {
    SPRING,SUMMER,FALL,WINTER
}
View Code

运行结果:

[SPRING, SUMMER, FALL, WINTER]
[]
[SPRING, WINTER]
[SUMMER, WINTER]
[SUMMER, FALL, WINTER]
[SPRING]

5.各Set集合性能比较

四.List集合

List作为Collection接口的子接口,当然可以使用Collection接口里全部方法。而且由于List是有序集合,因此List集合里增加了一些根据索引来操作集合元素的方法,具体方法见Java API文档。

import java.util.*;
public class ListTest {
    public static void main(String[] args) {
        //创建集合对象
        List books=new ArrayList();
        
        //添加
        books.add("Java");
        books.add("C++");
        books.add("JavaScript");
        System.out.println(books);
        
        //将新字符串插入在第二个位置
        books.add(1, new String("Ajax"));
        for(int i=0; i<books.size(); i++) {
            System.out.println(books.get(i));
        }
        
        //删除第三个元素
        books.remove(2);
        System.out.println(books);
        
        //判断指定元素在List集合中的位置
        System.out.println(books.indexOf(new String("Ajax")));
        
        //将第二个元素替换为新的字符串
        books.set(1, new String("PHP"));
        System.out.println(books);
        
        //截取子集合,第二个元素(包括)到第三个元素(不包括)
        System.out.println(books.subList(1, 2));
    }
}
View Code

运行结果:

[Java, C++, JavaScript]
Java
Ajax
C++
JavaScript
[Java, Ajax, JavaScript]
1
[Java, PHP, JavaScript]
[PHP]

1.ListIterator接口

List集合除了提供Iterator接口外,还提供了ListIterator接口,ListIterator接口在Iterator接口基础上增加了如下方法:

import java.util.*;
public class ListIteratorTest {
    public static void main(String[] args) {
        String[] books={"Java","C++"};
        List booksList=new ArrayList();
        for(int i=0; i<books.length; i++) {
            booksList.add(books[i]);
        }
        
        //ListIterator接口
        ListIterator lit=booksList.listIterator();
        while(lit.hasNext()) {
            System.out.println(lit.next());
            lit.add("------分隔符------");
        }
        System.out.println("======反向迭代======");
        while(lit.hasPrevious()) {
            System.out.println(lit.previous());
        }
    }
}
View Code

运行结果:

Java
C++
======反向迭代======
------分隔符------
C++
------分隔符------
Java

从上面程序可以看出,使用ListIterator迭代List集合时,开始也需要采用正向迭代,即先使用next()方法进行迭代,在迭代过程中可以使用add()方法向上依次迭代元素后面添加一个新元素。

2.ArrayList和Vector实现类

ArrayList和Vector用法几乎完全相同,但有个显著区别:ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,如果超过一个线程修改ArrayList集合,则程序必须手动保证集合的同步性;但Vector集合则是线程安全的。因为Vector是线程安全的,所以性能比ArrayList要低。实际上,即使需要保证List集合线程安全,也同样不推荐使用Vector实现类。后面会介绍一个Collections工具类,它可以将一个ArrayList变成线程安全的。

Vector还提供了一个Stack子类,用于模拟栈的数据结构,提供了如下方法:

import java.util.*;
public class VectorTest {
    public static void main(String[] args) {
        Stack v=new Stack();
        v.push("Java");
        v.push("C++");
        v.push("PHP");
        System.out.println(v);
        
        System.out.println(v.peek());
        System.out.println(v);
        
        System.out.println(v.pop());
        System.out.println(v);
        
    }
}
View Code

运行结果:

[Java, C++, PHP]
PHP
[Java, C++, PHP]
PHP
[Java, C++]

五.Queue集合

1.PriorityQueue实现类

PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此当调用peek()方法或poll()方法取出队列元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。

import java.util.*;
public class PriorityQueueTest {
    public static void main(String[] args) {
        PriorityQueue pq=new PriorityQueue();
        //添加
        pq.offer(6);
        pq.offer(-3);
        pq.offer(9);
        pq.offer(0);
        
        //输出
        System.out.println(pq);
        
        //访问第一个元素
        System.out.println(pq.peek());
    }
}
View Code

运行结果:

[-3, 0, 9, 6]
-3
PriorityQueue不允许插入null元素,它需要对队列元素进行排序,PriorityQueue的元素有两种排序方式:自然排序和定制排序。PriorityQueue队列对元素的要求和TreeSet对元素的要求基本一致,因此关于使用自然排序和定制排序参考TreeSet。

2.Deque接口与ArrayDeque实现类

Deque接口是Queue接口的子接口,它代表一个双端队列,Deque接口里定义方法允许从两端来操作队列的元素。另外,Deque不仅可以当成双端队列使用,而且可以当成栈来使用,所以当需要使用栈这种数据结构时,推荐使用ArrayDeque或LinkedList,而不是Stack。

import java.util.*;
/**
 * 将ArrayDeque当成栈使用
 */
public class ArrayDequeTest {
    public static void main(String[] args) {
        ArrayDeque st=new ArrayDeque();
        //元素push进栈
        st.push("Java");
        st.push("C++");
        st.push("PHP");
        //输出
        System.out.println(st);
        //访问第一个元素
        System.out.println(st.peek());
        System.out.println(st);
        //pop出第一个元素
        System.out.println(st.pop());
        System.out.println(st);
    }
}
View Code

运行结果:

[PHP, C++, Java]
PHP
[PHP, C++, Java]
PHP
[C++, Java]

3.LinkedList实现类

LinkedList类是List接口的实现类,这就意味着它是一个List集合,可以根据索引随机访问集合中的元素。除此之外,LinkedList还实现了Deque接口,因此它可以当作双端队列使用,自然也可以当作栈使用。

import java.util.*;
public class LinkedListTest {
    public static void main(String[] args) {
        LinkedList books=new LinkedList();
        //将字符串加入队列尾部
        books.offer("Java");
        
        //将字符串加入栈顶部
        books.push("C++");
        
        //将字符串加入队列的头部
        books.offerFirst("PHP");
        
        //输出
        for(int i=0; i<books.size(); i++) {
            System.out.println(books.get(i));
        }
        
        //访问但不擅长栈顶元素
        System.out.println(books.peekFirst());
        
        //访问但不删除队列的最后一个元素
        System.out.println(books.peekLast());
        
        //将栈顶元素出栈
        System.out.println(books.pop());
        
        //输出
        System.out.println(books);
        
        //访问并删除队列最后一个元素
        System.out.println(books.pollLast());
        
        //输出
        System.out.println(books);
    }
}
View Code

运行结果:

PHP
C++
Java
PHP
Java
PHP
[C++, Java]
Java
[C++]

4.性能比较

(1).如果需要遍历List集合元素,对于ArrayList、Vector集合,应该使用随机访问方法(get)来遍历集合元素,这样性能更好;对于LinkedList集合,则应使用迭代器(Iterator)来遍历集合元素。

(2).如果需要经常执行插入、删除操作来改变List集合的大小,则应该使用LinkedList集合,而不是ArrayList。

(3).如果有多个线程需要同时访问List集合中的元素,开发者可考虑使用Collections将集合包装成线程安全的集合。

六.Map集合

Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组用于保存Map里的value,key和value之间存在单向的一对一的关系,即通过指定的key,总能找到唯一的、确定的value。

1.HashMap和Hashtable实现类

HashMap和Hashtable存在两点显著区别:

①Hashtable是线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable性能高一点。

②Hashtable不允许使用null作为key和value,但HashMap可以使用null作为key或value。

HashMap、Hashtable判断两个key相等的标准是:两个key通过equals()方法比较返回true,两个key的hashCode值也相等。

HashMap、Hashtable判断两个value相等的标准是:只要两个对象同equals()方法比较返回true。

import java.util.*;
public class HashtableTest {
    public static void main(String[] args) {
        Hashtable ht=new Hashtable();
        ht.put(new A(60000), "Java");
        ht.put(new A(87563), "C++");
        ht.put(new A(1232), new B());
        System.out.println(ht);
        
        //只要两个对象通过equals()方法比较返回true
        //Hashtable就认为它们是相等的value
        //由于Hashtable中有一个B对象
        //它与任何对象通过equals()方法比较相等,所以下面输出true
        System.out.println(ht.containsValue("测试字符串"));
        
        //只要两个A对象的count相等,它们通过equals()方法比较返回true,且hashCode值相等
        //Hashtable即认为它们是相同的key,所以输出true
        System.out.println(ht.containsKey(new A(87563)));
        
        //删除key-value对
        ht.remove(new A(1232));
    
        //通过返回Hashtable的所以key组成的Set集合
        //从而遍历Hashtable的每个key-value对
        for(Object key : ht.keySet() ) {
            System.out.print(key + "------>");
            System.out.println(ht.get(key));
        }
    }
}

class A {
    int count;
    
    public A(int count) {
        this.count=count;
    }
    
    public boolean equals(Object obj) {
        if(obj==this) {
            return true;
        }
        if(obj!=null && obj.getClass()==A.class) {
            A a=(A)obj;
            return (this.count==a.count);
        }
        return false;
    }
    
    public int hashCode() {
        return this.count;
    }
}

class B {
    public boolean equals(Object obj) {
        return true;
    }
}
View Code

运行结果:

{com.map.A@ea60=Java, com.map.A@1560b=C++, com.map.A@4d0=com.map.B@55e55f}
true
true
com.map.A@ea60------>Java
com.map.A@1560b------>C++

2.LinkedHashMap实现类

LinkedHashMap需要维护元素的插入顺序,因此性能低于HashMap的性能;但因为它以链表来维护内部的顺序,所以在迭代访问Map里的全部元素时有较好的性能。

import  java.util.*;
public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap scores=new LinkedHashMap();
        scores.put("语文", 80);
        scores.put("英语", 82);
        scores.put("数学", 76);
        
        //遍历scores中的key-value对
        for(Object key : scores.keySet() ) {
            System.out.println(key + "----->" +scores.get(key));
        }
        
        System.out.println("========================");
        //迭代遍历scores中的key-value对
        for(Iterator it=scores.keySet().iterator(); it.hasNext(); ) {
            Object key=it.next();
            System.out.println(key+ "------>"+scores.get(key));
        }
    }
}
View Code

运行结果:

语文----->80
英语----->82
数学----->76
========================
语文------>80
英语------>82
数学------>76

3.使用Properties读写属性文件

Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件,也可以把属性文件中的"属性名=属性值"加载到Map对象中。

import java.io.*;
import java.util.*;
public class PropertiesTest {
    public static void main(String[] args) throws Exception {
        Properties props=new Properties();
        //向Properties中添加属性 
        props.setProperty("username", "admin");
        props.setProperty("password", "123456");
        
        //将Properties中的key-value对保存到a.ini文件中
        props.store(new FileOutputStream("a.ini"), "comment line");
        
        //新建一个Properties对象
        Properties props2=new Properties();
        props2.setProperty("gender", "male");
        
        //将a.ini文件中的key-value对追加到props2中
        props2.load(new FileInputStream("a.ini"));
        
        System.out.println(props2);
    }
}
View Code

运行结果:

{password=123456, gender=male, username=admin}

上面程序还在当前路径下生成一个a.ini文件,该文件内容如下:

#comment line
#Wed Dec 11 15:52:48 CST 2013
password=123456
username=admin

4.SortedMap接口和TreeMap实现类

正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类一样,Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类。所以具体可以参考SortedSet和TreeSet。

import java.util.*;
public class TreeMapTest {
    public static void main(String[] args) {
        TreeMap tm=new TreeMap();
        tm.put(new R(3), "Java");
        tm.put(new R(-5), "C++");
        tm.put(new R(9), "PHP");
        System.out.println(tm);
        
        //返回第一个key-value对
        System.out.println(tm.firstEntry());
        
        //返回最后一个key值
        System.out.println(tm.lastKey());
        
        //返回比new R(2)大的最小的key值
        System.out.println(tm.higherKey(new R(2)));
        
        //返回比new R(2)小的最大的key-value对
        System.out.println(tm.lowerEntry(new R(2)));
        
        //返回子TreeMap
        System.out.println(tm.subMap(new R(-5), new R(4)));
    }
}

class R implements Comparable {
    int count;
    public R(int count) {
        this.count=count;
    }
    
    //@override
    public String toString() {
        return "R[count:"+count+"]";
    }
    
    //@override 
    public boolean equals(Object obj) {
        if(obj==this) {
            return true;
        }
        if(obj!=null && obj.getClass()==R.class) {
            R r=(R)obj;
            return (this.count==r.count);
        }
        return false;
    }
    
    //@Override
    public int compareTo(Object obj) {
        R r=(R)obj;
        return this.count>r.count ? 1 :
                this.count<r.count ? -1 : 0;
    }
}
View Code

运行结果:

{R[count:-5]=C++, R[count:3]=Java, R[count:9]=PHP}
R[count:-5]=C++
R[count:9]
R[count:3]
R[count:-5]=C++
{R[count:-5]=C++, R[count:3]=Java}

5.WeakHashMap实现类

WeakHashMap与HashMap的用法基本相似,与HashMap的区别在于,HashMap的key保留了对实际对象的强引用,但WeakHashMap的key只保留对实际对象的弱引用。

关于Java中强引用,软引用,弱引用和虚引用的介绍,请见http://java.chinaitlab.com/oop/716371.html 与 http://www.cnblogs.com/blogoflee/archive/2012/03/22/2411124.html

import java.util.*;
public class WeakHashMapTest {
    public static void main(String[] args) {
        WeakHashMap whm=new WeakHashMap();
        //三个key都是匿名的字符串对象,没有其他引用
        whm.put(new String("语文"), new String("良好"));
        whm.put(new String("数学"), new String("及格"));
        whm.put(new String("英语"), new String("中等"));
        
        //该key是一个系统缓存的字符串对象
        whm.put("Java", new String("优秀"));
        System.out.println(whm);
        
        //通知系统垃圾回收
        System.gc();
        System.runFinalization();
        System.out.println(whm);
    }
}
View Code

运行结果:

{Java=优秀, 数学=及格, 英语=中等, 语文=良好}
{Java=优秀}

6.IdentityHashMap实现类

IdentityHashMap实现类与HashMap基本相似,区别在于IdentityHashMap比较两个key相等是用==运算符,而HashMap是通过equals()方法。

import java.util.*;
public class IdentityHashMapTest {
    public static void main(String[] args) {
        IdentityHashMap ihm=new IdentityHashMap();
        ihm.put(new String("语文"), 89);
        ihm.put(new String("语文"), 89);
        
        ihm.put("Java", 93);
        ihm.put("Java", 98);
        
        System.out.println(ihm);
    }
}
View Code

运行结果:

{语文=89, Java=98, 语文=89}

7.EnumMap实现类

import java.util.*;
public class EnumMapTest {
    public static void main(String[] args) {
        EnumMap en=new EnumMap(Season.class);
        en.put(Season.SUMMER, "夏天");
        en.put(Season.SPRING, "春天");
        
        System.out.println(en);
    }
}

enum Season {
    SPRING,SUMMER,FALL,WINTER
}
View Code

运行结果:

{SPRING=春天, SUMMER=夏天}

8.性能比较

七.操作集合的工具类: Collections

Java提供了一个操作Set、List和Map等集合的工具类:Collections,该工具类提供了大量的方法对集合元素进行排序、查询和修改操作,还提供了将集合对象设置为不可变、对集合对象实现同步控制等方法。

1.排序

具体方法见Java API文档。

import java.util.*;
public class SortTest {
    public static void main(String[] args) {
        ArrayList nums=new ArrayList();
        nums.add(2);
        nums.add(-5);
        nums.add(3);
        nums.add(0);
        System.out.println(nums);
        
        //反转
        Collections.reverse(nums);
        System.out.println(nums);
        
        //排序
        Collections.sort(nums);
        System.out.println(nums);
        
        //随机打乱
        Collections.shuffle(nums);
        System.out.println(nums);
    }
}
View Code

运行结果:

[2, -5, 3, 0]
[0, 3, -5, 2]
[-5, 0, 2, 3]
[0, 2, 3, -5]

2.查找、替换操作

具体方法见Java API文档。

import java.util.*;
public class SearchTest {
    public static void main(String[] args) {
        ArrayList nums=new ArrayList();
        nums.add(2);
        nums.add(-5);
        nums.add(3);
        nums.add(0);
        System.out.println(nums);
        
        //输出最大,最小元素
        System.out.println(Collections.max(nums));
        System.out.println(Collections.min(nums));
        
        //用1替换0
        Collections.replaceAll(nums, 0, 1);
        System.out.println(nums);
        
        //返回-5出现的次数
        System.out.println(Collections.frequency(nums, -5));
        
        //二分查找-5在集合中的位置
        Collections.sort(nums);
        System.out.println(nums);
        System.out.println(Collections.binarySearch(nums, -5));
    }
}
View Code

运行结果:

[2, -5, 3, 0]
3
-5
[2, -5, 3, 1]
1
[-5, 1, 2, 3]
0

3.同步控制

Collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时线程安全的问题。

import java.util.*;
public class SynchronizedTest {
    public static void main(String[] args) {
        //创建List,Set,Map的线程安全版本
        List list=Collections.synchronizedList(new ArrayList());
        Set set=Collections.synchronizedSet(new HashSet());
        Map map=Collections.synchronizedMap(new HashMap());
    }
}
View Code

4.设置不可变集合

Collections提供了如下三类方法来返回一个不可变的集合:

import java.util.*;
public class UnmodifiableTest {
    public static void main(String[] args) {
        //创建一个空的、不可边的List对象
        List list=Collections.emptyList();
        
        //创建一个只有一个元素,不变的Set对象
        Set set=Collections.singleton("Java");
        
        //创建一个普通的Map对象
        Map scores=new HashMap();
        scores.put("Chinese", 80);
        scores.put("English", 90);
        
        //返回普通的Map对象对应的不可变版本
        Map unmodifiableMap=Collections.unmodifiableMap(scores);
        
        //下面将出现异常
        list.add("Test");
        set.add("Test");
        unmodifiableMap.put("Math", 100);
    }
}
View Code
posted @ 2013-12-13 17:28  顺冉  阅读(733)  评论(2编辑  收藏  举报