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); } }
运行结果:
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); } }
运行结果:
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); } } } }
运行结果:
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); } } }
运行结果:
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); } }
运行结果:
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; } }
运行结果:
[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); } }
运行结果:
[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)); } }
运行结果:
[-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; } }
运行结果:
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; } }
运行结果:
[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+"]"; } }
运行结果:
[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 }
运行结果:
[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)); } }
运行结果:
[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()); } } }
运行结果:
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); } }
运行结果:
[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()); } }
运行结果:
[-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); } }
运行结果:
[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); } }
运行结果:
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; } }
运行结果:
{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)); } } }
运行结果:
语文----->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); } }
运行结果:
{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; } }
运行结果:
{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); } }
运行结果:
{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); } }
运行结果:
{语文=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 }
运行结果:
{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); } }
运行结果:
[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)); } }
运行结果:
[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()); } }
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); } }