Java中的集合类
(Collection framework)我们在前面学习过java数组,java数组的程度是固定的,在同一个数组中只能存放相同的类型数据。数组可以存放基本类型的数据,也可以存入对象引用的数据。
在创建数组时,必须明确指定数组的长度,数组一旦创建,其长度就不能改变,在许多应用的场合,一组数据的数目不是固定的,比如一个单位的员工数目是变化的,有老的员工跳槽,也有新的员工进来。
为了使程序方便地存储和操纵数目不固定的一组数据,JDK中提供了 java集合类,所有java集合类都位于java.util包中,与java数组不同,java集合类不能存放基本数据类型数据,而只能存放对象的引用。
Java集合类分为三种
Set(集合):集合中对象不按特定的方式排序。并且没有重复对象,但它有些实现类中的对象按特定方式排序。--无序,不能重复
List(列表):集合中的对象按照检索位置排序,可以有重复对象,允许按照对象在集中的索引位置检索对象,List和数组有些相似。--有序,可以重复
Map(映射):集合中的每一个元素包含一对键对象和值对象,集合中没有重复的键对象,值对象可以重复,它的有些实现类能对集合中的键对象进行排序。
Java的主要集合类的框架图
Collection和Iterator接口
在Collection接口中声明了适用于java集合(只包括Set和List)通用方法。
Collection接口的方法
方法 | 描述 |
boolean add(Object o) | 向集合中加入一个对象的引用 |
void clear( ) | 删除集合中所有对象,即不再对持有对象的引用 |
boolean contains(Object o) | 判断在集合中是否含有特定对象的引用 |
boolean isEmpty() | 判断集合是否为空 |
Iterator iterator( ) | 返回一个Iterator对象,可用它来遍历集合中的元素 |
boolean remove(Object o) | 从集合中删除一个对象的引用 |
int size( ) | 返回集合中元素的数目 |
Object [ ] toArray() | 返回一个数组,该数组包含集合中的所有元素 |
Set接口和List即可都继承了Collection接口,而Map接口没有继承Collection接口,因此可以对Set对象和List对象调用以上方法,但是不能对Map对象调用以上方法。
Collection接口的iterator()和toArray()方法多用于获得集合中的所有元素,前者返回一个Iterator对象,后者返回一个包含集合中所有元素的数组。
Iterator隐藏底层集合的数据结构,向客户程序提供了遍历各种类型的集合的统一接口。
Iterator接口中声明了如下方法:
l hasNext():判断集合中的元素是否遍历完毕,如果没有,就返回true。
l next():返回下一个元素
l remove():从集合中删除上一个由next()方法返回的元素。
注意:如果集合中的元素没有排序,Iterator遍历集合中元素的顺序是任意的,并不一定与像集合中加入的元素的顺序一致。
Set(集)
Set是最简单的一种集合,集合中的对象不按特定方式排序,并没有重复对象。Set接口主要有两个实现类:HashSet类还有一个子类LinkedHashSet类,它不仅实现了哈希算法,而且实现了链表数据结构,链表数据结构能提高插入核算出元素的性能。TreeSet类实现了SortedSet接口中,具有排序功能。
List(列表)
List的主要特征使其元素已先行方式存储,集合中允许存放重复对象。List接口主要的实现类包括:
l ArrayList—ArrayList代表长度可变的数组。允许对元素进行快速的随机访问,但是向ArrayList中插入与删除元素的速度较慢。
l LinkedList—在实现中采用链表数据结构。对顺序访问进行了优化,向List中插入和删除元素的速度较快,随机访问速度则相对较慢,随机访问是指检索位于特定索引位置元素。
Map(映射)
Map(映射)是一种吧键对和值对象进行映射的集合。它的每一个元素都包含一对键对象和值对象,而之对象仍可以是Map类型。以此类推,这样就形成了多级映射。向Map集合中加入元素时,必须提供一对键对象和值对象,从Map集合上检索元素只要给出键对象,就会返回值对象。
实例1
CollectionAll.java
package collection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Hashtable; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet;
public class CollectionAll { public static void main(String args[]){ List list1=new LinkedList();
list1.add("我"); list1.add("是"); list1.add("谁"); list1.add("我"); traverse(list1);
List list2=new ArrayList(); list2.add("你"); list2.add("是"); list2.add("谁"); list2.add("你"); traverse(list2);
Set set1=new HashSet(); set1.add("他"); set1.add("是"); set1.add("谁"); set1.add("他"); traverse(set1);
SortedSet set2=new TreeSet(); set2.add("您"); set2.add("是"); set2.add("谁"); set2.add("您"); System.out.println(set2.size()); traverse(set2);
LinkedHashSet set3=new LinkedHashSet(); set3.add("尔"); set3.add("是"); set3.add("谁"); set3.add("尔"); traverse(set3);
Map m1=new HashMap(); m1.put("name05","我"); m1.put("name06","是"); m1.put("name08","谁"); m1.put("name09","我"); traverse(m1.keySet()); traverse(m1.values());
SortedMap m2=new TreeMap(); m2.put("name01","我"); m2.put("name02","是"); m2.put("name03","谁"); m2.put("name04","我"); traverse(m2.keySet()); traverse(m2.values());
LinkedHashMap m3=new LinkedHashMap(); m3.put("name01","我"); m3.put("name02","是"); m3.put("name03","谁"); m3.put("name04","我"); traverse(m3.keySet()); traverse(m3.values());
Hashtable numbers=new Hashtable(); numbers.put("name01","我"); numbers.put("name02","是"); numbers.put("name03","谁"); numbers.put("name04","我"); traverse(numbers.keySet()); traverse(numbers.values()); } static void traverse(Collection coll) { Iterator iter=coll.iterator(); while(iter.hasNext()){ String elem=(String)iter.next(); System.out.print(elem+" "); } System.out.println(); } } |
一. 集合框架中的各种实现类
HashSet类
HashSet类按照哈希算法---存取集合中的对象,具有很好的存取和查找功能。当向集合中加入一个对象时,HashSet对调用对象的hashCode()方法来多的哈希码,然后根据这个哈希码进一步计算出对象在集合中的存放位置。
实例2 MyHashSet.java
package collection; import java.util.*; public class MyHashSet{ public static void main(String args[]){ HashSet set=new HashSet(6); Object[] values={"Tom","Mike","Mary","Linda","Jone","Jack"}; for(int i=0;i<values.length;i++){ values[i].hashCode(); set.add(values[i]); System.out.println(set.hashCode()); } set.remove("Mike"); System.out.println("size="+set.size()); Iterator iter=set.iterator(); while(iter.hasNext()){ String elem=(String)iter.next(); System.out.println(elem+" "); } System.out.println(set.contains("Jack")); System.out.println(set.contains("Linda")); System.out.println(set.contains("Mike")); } } |
TreeSet类
TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序,一下程序创建了一个TreeSet对象,然后向集合中加入了4个Integer对象
实例3
TreeSetTest.java
package collection; import java.util.*; public class TreeSetText{ public static void main(String args[]) { Set set=new TreeSet(); set.add(new Integer(8)); set.add(new Integer(7)); set.add(new Integer(6)); set.add(new Integer(9)); Iterator it=set.iterator(); while(it.hasNext()) System.out.println(it.next()+""); } } |
由此我们知道当TreeSet集合中加入一个对象时,会把它插入到有序的对象序列中。
ArrayList类
ArrayList—ArrayList代表长度可变的数组。允许对元素进行快速的随机访问,它允许所有元素,有包括null。ArrayList是线程不同步的,向ArrayList中插入与删除元素的速度较慢
LinkedList类
LinkedList—实现了List接口,允许null元素,在实现中采用链表数据结构。对顺序访问进行了优化,向List中插入和删除元素的速度较快,随机访问速度则相对较慢,随机访问是指检索位于特定索引位置元素。
注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须实现访问同步。
一种解决方法是在创建List时构造一个同步的List:
List list=Collection.synchronizedList(new List(…));
实例4
StackL.java
package collection; import java.util.LinkedList; public class StackL{ private LinkedList list=new LinkedList(); public void push(Object v){ list.addFirst(v); } public Object top() { return list.getFirst(); } public Object pop() { return list.removeFirst(); } public static void main(String args[]) { StackL stack=new StackL(); for(int i=0;i<10;i++) stack.push(new Integer(i)); System.out.println(stack.top()); System.out.println(stack.top()); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); } } |
ListIterator接口
List的listIterator()方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,此外还提供了专门操控列表的方法。
l add():向列表插入一个元素
l hasNext():判断列表中是否还有下一个元素
l hasPrevious():判断列表中是否还有上一个元素
l next():返回列表中的下一个元素
l previous():返回列表中的上一个元素
实例5
ListIterator.java
import java.util.*; public class ListInserter{ public static void insert(List list,int data){ ListIterator it=list.listIterator(); while(it.hasNext()) { Integer in=(Integer)it.next(); if(data<=in.intValue()){ it.previous(); it.add(new Integer(data)); break; } } } public static void main(String args[]){ List list=new LinkedList(); list.add(new Integer(3)); list.add(new Integer(2)); list.add(new Integer(5)); list.add(new Integer(9)); Collections.sort(list); insert(list,6); ListIterator it=list.listIterator(); while(it.hasNext()){ Integer elem=(Integer)it.next(); System.out.println(elem+""); } } } |
实例6 比较java数组和各种List的性能
PerformanceTester.java
package collection; import java.util.*; public class PerformanceTester{ private static final int TIMES=100000; public static abstract class Tester { private String operation; public Tester(String operation){this.operation=operation;} public abstract void test(List list); public String getOperation(){return operation;} } static Tester iterateTester=new Tester("iterate"){ public void test(List list){ for(int i=0;i<10;i++){ Iterator it=list.iterator(); while(it.hasNext()) {it.next();} } } }; static Tester getTester=new Tester("get"){ public void test(List list) { for(int i=0;i<list.size();i++) for(int j=0;j<10;j++) list.get(j); } }; static Tester insertTester=new Tester("insert"){ public void test(List list) { ListIterator it=list.listIterator(list.size()/2); for(int i=0;i<TIMES/2;i++) it.add("hello"); } }; static Tester removeTester=new Tester("remove") { public void test(List list) { ListIterator it=list.listIterator(); while(it.hasNext()) { it.next(); it.remove(); } } }; static public void testJavaArray(List list) { Tester[]testers={iterateTester,getTester}; test(testers,list); } static public void testList(List list){ Tester[]testers={insertTester,iterateTester,removeTester}; test(testers,list); } static public void test(Tester[]testers,List list){ for(int i=0;i<testers.length;i++){ System.out.print(testers[i].getOperation()+"操作:"); long t1=System.currentTimeMillis(); testers[i].test(list); long t2=System.currentTimeMillis(); System.out.println(t2-t1+"ms"); System.out.println(); } } public static void main(String args[]){ List list=null; System.out.println("---测试java数组---"); String ss[]=new String[TIMES]; Arrays.fill(ss,"hello"); list=Arrays.asList(ss); testJavaArray(list); ss=new String[TIMES/2]; Collection col=Arrays.asList(ss); System.out.println("---测试Vector---"); list=new Vector(); list.addAll(col); testList(list); System.out.println("---测试LinkedList---"); list=new LinkedList(); list.addAll(col); testList(list); System.out.println("---测试ArrayList---"); list=new ArrayList(); list.addAll(col); testList(list); } } |
从结果可以看出,对java数组进行随机访问和迭代操作具有最快的速度;对LinkedList进行插入和删除具有最快的速度;对ArrayList进行随机访问也具有较快的速度,Vector类在各个方面都没有突出的性能描述与历史集合类,已经不提倡使用它。
Map
Map(映射)是一种把键对象和值对象进行映射的集合。它的每一个元素都包含一对键对象和值对象,而值对象仍可以是Map类型。以此类推,这样就形成了多级映射。向Map集合中加入元素,必须提供一对键对象和值对象,从Map集合上检索元素只要给出键对象,就会返回对应的值对象。
HashMap类
HashMap类按照哈希算法—存取Map中的对象,允许存储空对象,而且键是空
实例7 Statistics.java
package collection; import java.util.HashMap; import java.util.Map; import java.util.Random; class Counter{ int i=1; public String toString(){ return Integer.toString(i); } } public class Statistics{ private static Random rand=new Random(); public static void main(String args[]){ Map hm=new HashMap(); for(int i=0;i<1000;i+=1){ Integer r=new Integer(rand.nextInt(20)); if(hm.containsKey(r)) ((Counter)hm.get(r)).i++; else hm.put(r,new Counter()); } System.out.println(hm); } } |
Hashtable
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可以作为key或者value。
实例8 HashTable.java
import java.util.Enumeration; import java.util.Hashtable; public class HashTable{ public static void main(String args[]){ String names[]={"张三","李四","王五","赵六","陈七","孙九"}; float diameters[]={4800f,12103.6f,12756.3f,6794f,142984f,120536f}; Hashtable hash=new Hashtable(); for(int i=0,n=names.length;i<n;i++){ hash.put(names[i],new Float(diameters[i])); } Enumeration e=hash.keys(); Object obj; while(e.hasMoreElements()){ obj=e.nextElement(); System.out.println(obj+":"+hash.get(obj)); } } } |
HashMap和HashTable的区别
HashTable继承自Dictionary,而HashMap是Map interface的一个实现
HashMap允许将null作为一个key或者value,而HashTable不允许。
还有就是,HashMap把HashTable的contains方法去掉了,才成containsvalue和containsKey。
最大的不同是,HashTable的方法是synchronize的,而HashMap不是,在多个线程访问HashTable时,不需要自己为它的方法实现同步而HashMap就必须为之提供外同步。
TreeMap
实例9 SortTreeMap.Java
package collection; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; public class SortTreeMap{ public static void main(String args[]) { String names[]={"B","E","A","M","J","C","H","D","G"}; float diameters[]={4800f,12103.6f,12756.3f,6794f,142984f,120536f,51118f,49532f,2274f}; Map map=new TreeMap(); for(int i=0,n=names.length;i<n;i++){ map.put(names[i],new Float(diameters[i])); } Iterator it=map.keySet().iterator(); Object obj; while(it.hasNext()){ obj=it.next(); System.out.println(obj+":"+map.get(obj)); } } } |
二. 集合实用类:Collections
在java集合中,有一个使用类,即java.util.Collections,它的一部分方法专门用于操作List类型集合,还有一部分方法用于操纵所有的Collection类型或map类型的集合。
以下方法适用于Collection类型或者map类型的集合。
实例10 Collection.java
package collection; import java.util.*; public class collection{ public static void main(String args[]) { ArrayList arrayList1=new ArrayList(); arrayList1.add(new Integer(1)); arrayList1.add(new Integer(9)); arrayList1.add(new Integer(3)); arrayList1.add(new Integer(7)); arrayList1.add(new Integer(8)); arrayList1.add(new Integer(2)); arrayList1.add(new Integer(5)); arrayList1.add(new Integer(4)); arrayList1.add(new Integer(6)); System.out.println("目前ArrayList数据内容如下"); System.out.println("arrayList1"); Collections.sort(arrayList1); System.out.println("排序后的内容如下:"+arrayList1); Collections.reverse(arrayList1); System.out.println("反排序的内容如下:"+arrayList1); Collections.shuffle(arrayList1); System.out.println("重新洗牌后数据内容如下:"+Collections.max(arrayList1)); System.out.println("最大值"+Collections.max(arrayList1)); System.out.println("最小值"+Collections.min(arrayList1)); } } |
三. 集合类中排序
在jdk类库中,有一部分类实现了Comparable接口,如Integer,Double和String等。Comparable接口有一个compareTo(Object o)方法,它返回整数类型。对于表达数x.compareTo(y),如果返回值为0,则表示x和y相等,如果返回值大于0,则表示x大于y,如果返回小于0,则表示x小于y。
在TreeSet对象调用compareTo()方法比较及各种对象的大小。
实例11
package collection; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; public class ComparatorTest implements Comparable{ String firstName,lastName; public Person(String f,String l){ this.firstName=f; this.lastName=l; } public String getFirstName(){ return firstName; } public String getLastName(){ return lastName; } public String toString(){ return "[name="+firstName+",name="+lastName+"]"; } public int compareTo(Object obj){ Person emp=(Person)obj; int deptComp=firstName.compareTo(emp.getFirstName()); return((deptComp==0)?lastName.compareTo(emp.getLastName()):deptComp); } public boolean equals(Object obj){ if(!(obj instanceofPerson)){ return false; } Person emp=(Person)obj; return firstName.equals(emp.getFirstName())&&lastName.equals(emp.getLastName()); } } public class ComparatorTest implements Comparator { public int compare(Object obj1,Object obj2){ Person emp1=(Person)obj1; Person emp2=(Person)obj2; int nameComp=emp1.getFirstName().compareTo(emp2.getFirstName()); return ((nameComp=0)?emp1.getLastName().compareTo(emp2.getLastName()):nameComp); } public static void main(String args[]){ String name[]={"张三","李四","王五","赵六","陈七","孙八","周九"};
//Convert to list List list=new ArrayList(Array.asList(names));
//Ensure list sorted Collections.sort(list); System.out.println("List排序:[length:"+list.size()+"]"); System.out.println(list);
//Search for element in list int index=Collections.binarySearch(list,"李四"); System.out.println("发现位置"+index);
//Search for element not in list index=Collections.binarySearch(list,",楚一"); System.out.println("没有发现楚一"+index);
//insert int newIndex=-index-1; list.add(newIndex,"冯二"); System.out.println("增加了冯二:[length:"+list.size()+"]"); System.out.println(list);
//Min should be Bart System.out.println(Collections.min(list));
//Max should be Roy System.out.println(Collections.max(list));
Comparator comp=Collections.reverseOrder();
//Reversed Min should be Roy System.out.println(Collections.min(list,comp));
//Reversd Max should be Bart System.out.println(Collections.max(list,comp)); } } |
四. 集合类历史
在早期jdk1.0版本中,代表集合的类只有Vector,Stack,Enumeration,Hashtable,Properties。从jdk1.2版本中开始,才出现了Collection,set,list和map接口以及各种实现类。他们构成了完整的集合框架。JDK1.0版本中的集合类也成为历史集合类
历史集合类
历史集合类 | 描述 | 缺点 | 新框架集合类的替代类 |
Vector | 集合类中的元素有索引位置,在新的集合框架中把它改为实现了List接口 | 采用了同步机制,影响操纵集合的性能 | ArrayList和LinkedList |
Stack | 表示堆栈,支持后进先出的操作 | 采用了同步机制,影响操纵集合的性能:Stack继承了Vector类,使得Stack不能作为严格的堆栈,还允许随机访问 | LinkedList |
Hashtable | 集合中的每一个元素包含一对键与值。在新的集合框架中把它改为实现了Map接口 | 采用了同步机制,影响了操纵集合的性能 | HashMap |
Properties | 集合中的每个元素包含了一对键与值,继承了Hashtable | 采用了同步机制,影响了操纵集合的性能 | 无 |
Enumeration | 用于遍历集合中元素 | 只能与Vector和Hashtable等历史集合配套使用Enumeration类的名字较长,没有Iterator类名简短 | Iterator |
从JDK1.2版本开始,对Hashtable和Vector做了修改,是他们分别实现了List和Map接口。尽管如此,由于Vector ,Stack,Enumeration和Hashtable,在实现中使用了同步机制,并发性能差,因此不提倡使用它们,Properties类是一种特殊的Map类,他继承了Hashtable(Object,Object)类。Properties类的load()方法可用来从输入流中中读取键与值
实例 12
myapp.properties
color=red shape=circle user=Tom |
PropertiesTester.java
package collection; import java.util.*; import java.io.*; public class PropertiesTester{ public static void print(Properties ps){ Set keys=ps.keySet(); Iterator it=keys.iterator(); while(it.hasNext()){ String key=(String)it.next(); String value=ps.getProperty(key); System.out.println(key+"="+value); } } public static void main(String args[])throws IOException{ Properties ps=new Properties(); InputStream in=PropertiesTester.class.getResourceAsStream("myapp.properties"); ps.load(in); print(ps); ps=System.getProperties(); print(ps); } } |
五. 小结
我们介绍了集中常用java集合类的特点点和使用方法,为了保证集合正常工作,有些集合类对存放的对象有特殊要求:
HashSet 和HashMap具有好的性能,是Set和Map首选的实现类,是有在需要排序的场合,才考虑用TreeSet和TreeMap。LinkedList和ArrayList各有优缺点,如果经常对元素之间插入和删除操作,那么可用LinkedList,如果经常随机访问元素,那么可用ArrayList。
六. 课后习题
1. Set和List有哪些区别?
2. Collection与Collections 有什么区别?
3. 比较java数组,ArrayList和LinkedList在查询和存取元素方面的性能
4. HashMap和HashTable的区别?
5. 编写一个程序,读取一系列名字,并将他们存储在LinkedList中,不能存储重复的名字,并允许用户查找一个名字。