java.util包详解 | 您所在的位置:网站首页 › java怎么导入所有包里 › java.util包详解 |
介绍Java的实用工具类库java.util包。在这个包中,Java提供了一些实用的方法和数据结构。本章介绍Java的实用工具类库java.util包。在这个包中,Java提供了一些实用的方法和数据结构。例如,Java提供日期(Data)类、日历(Calendar)类来产生和获取日期及时间,提供随机数(Random)类产生各种类型的随机数,还提供了堆栈(Stack)、向量(Vector) 、位集合(Bitset)以及哈希表(Hashtable)等类来表示相应的数据结构。 图8.1给出了java.util包的基本层次结构图。下面我们将具体介绍其中几个重要的类。 ┌java.util.BitSet │java.util.Calendar │ └java.util.GregorianCalendar │java.util.Date │java.util.Dictionary │ └java.util.Hashtable │ └java.util.Properties │java.util.EventObject │java.util.ResourceBundle ┌普通类┤ ├java.util.ListResourceBundle │ │ └java.util.PropertyResourceBundle │ │java.util.Local │ │java.util.Observable │ │java.util.Random │ │java.util.StringTokenizer │ │java.util.Vector │ │ └java.util.Stack Java.util┤ └java.util.TimeZone │ └java.util.SimpleTimeZone │ ┌java.util.Enumeration ├接 口┤java.util.EventListener │ └java.util.Observer │ ┌java.util.EmptyStackException └异常类┤java.util.MissingResourceException │java.util.NoSuchElementException └java.util.TooManyListenersException 图8.1 java.util包的基本层次结构8.2 日期类Date Java在日期类中封装了有关日期和时间的信息,用户可以通过调用相应的方法来获取系统时间或设置日期和时间。Date类中有很多方法在JDK1.0公布后已经过时了,在8.3中我们将介绍JDK1.0中新加的用于替代Date的功能的其它类。 在日期类中共定义了六种构造函数。 (1)public Date() 创建的日期类对象的日期时间被设置成创建时刻相对应的日期时间。 例 Date today=new Date();//today被设置成创建时刻相对应的日期时间。 (2)public Date (long date) long 型的参数date可以通过调用Date类中的static方法parse(String s)来获得。 例 long l=Date.parse("Mon 6 Jan 1997 13:3:00"); Date day=new Date(l); //day中时间为1997年 1月6号星期一,13:3:00。 (3)public Date(String s) 按字符串s产生一日期对象。s的格式与方法parse中字符串参数的模式相同。 例 Date day=new Date("Mon 6 Jan 1997 13:3:00"); //day 中时间为1997年1月6号星期一,13:3:00. (4)public Date(int year,int month,int date) (5)public Date(int year,int month,int date,int hrs,int min) (6)public Date(int year,int month,int date,int hrs,int min,int sec) 按给定的参数创建一日期对象。 参数说明: year的值为:需设定的年份-1900。例如需设定的年份是1997则year的值应为97,即1997-1900的结果。所以Date中可设定的年份最小为1900; month的值域为0~11,0代表1月,11表代表12月; date的值域在1~31之间; hrs的值域在0~23之间。从午夜到次日凌晨1点间hrs=0,从中午到下午1点间hrs=12; min和sec的值域在0~59之间。 例 Date day=new Date(11,3,4); //day中的时间为:04-Apr-11 12:00:00 AM另外,还可以给出不正确的参数。 例 设定时间为1910年2月30日,它将被解释成3月2日。 Date day=new Date(10,1,30,10,12,34); System.out.println("Day's date is:"+day); //打印结果为:Day's date is:Web Mar 02 10:13:34 GMT+08:00 1910 下面我们给出一些Date类中常用方法。 (1)public static long UTC(int year,int month,int date,int hrs. int min,int sec) 该方法将利用给定参数计算UTC值。UTC是一种计时体制,与GMT(格林威治时间)的计时体系略有差别。UTC计时体系是基于原子时钟的,而GTMT计时体系是基于天文学观测的。计算中使用的一般为GMT计时体系。 (2)public static long parse(String s) 该方法将字符串s转换成一个long型的日期。在介绍构造方法Date(long date)时曾使用过这个方法。 字符串s有一定的格式,一般为: (星期 日 年 时间GMT+时区) 若不注明时区,则为本地时区。 (3)public void setMonth(int month) (4)public int getMonth() 这两个方法分别为设定和获取月份值。 获取的月份的值域为0~11,0代表1月,11代表12月。 (5)public String toString() (6)public String toLocalString() (7)public String toGMTString() 将给定日期对象转换成不同格式的字符串。它们对应的具体的格式可参看例子8.1。 (8)public int getTimezoneOffset() 该方法用于获取日期对象的时区偏移量。 例8.1中对上面介绍的Date类中的基本方法进行了具体的应用,并打印了相应的结果。由于使用了一些过时的方法,所以编译时会有警告信息。另外,由于本例中的时间表示与平台有关,不同的JDK版本对此处理不完全相同,因此不同版本的JDK执行本例的结果可能有细微差异。 例8.1 DateApp.java import java.lang.System; import java.util.Date; public class DateApp{ public static void main(String args[]){ Date today=new Date(); //today中的日期被设成创建时刻的日期和时间,假设创建时刻为1997年3月 //23日17时51分54秒。 System.out.println("Today's date is "+today); //返回一般的时间表示法,本例中结果为 //Today's date is Fri May 23 17:51:54 1997 System.out.println("Today's date(Internet GMT)is:" +today.toGMTString()); //返回结果为GMT时间表示法,本例中结果为 //Today's date(Internet GMT)is: 23 May 1997 09:51:54:GMT System.out.println("Today's date(Locale) is:" +today.toLocaleString()); //返回结果为本地习惯的时间表示法,结果为 //Today's date(Locale)is:05/23/97 17:51:54 System.out.println("Today's year is: "+today.getYear()); System.out.println("Today's month is: "+(today.getMonth()+1)); System.out.println("Today's date is: "+today.getDate()); //调用Date类中方法,获取年月日的值。 //下面调用了不同的构造方法来创建Date类的对象。 Date day1=new Date(100,1,23,10,12,34); System.out.println("Day1's date is: "+day1); Date day2=new Date("Sat 12 Aug 1996 13:3:00"); System.out.println("Day2's date is: "+day2); long l= Date.parse("Sat 5 Aug 1996 13:3:00 GMT+0800"); Date day3= new Date(l); System.out.println("Day3's date(GMT)is: "+day3.toGMTString()); System.out.println("Day3's date(Locale)is: " +day3.toLocaleString()); System.out.println("Day3's time zone offset is:" +day3.getTimezoneOffset()); } } 运行结果(JDK1.3版,与原文不同,原文是JDK1.0版): E:/java/tutorial/java01>java DateApp Today's date is Thu Dec 27 17:58:16 CST 2001 Today's date(Internet GMT)is:27 Dec 2001 09:58:16 GMT Today's date(Locale) is:2001-12-27 17:58:16 Today's year is: 101 Today's month is: 12 Today's date is: 27 Day1's date is: Wed Feb 23 10:12:34 CST 2000 Day2's date is: Fri Aug 12 13:03:00 CST 1996 Day3's date(GMT)is: 5 Aug 1996 05:03:00 GMT Day3's date(Locale)is: 1996-8-5 13:03:00 Day3's time zone offset is:-480 E:/java/tutorial/java01>8.3 日历类Calendar 在早期的JDK版本中,日期(Date)类附有两大功能:(1)允许用年、月、日、时、分、秒来解释日期:(2)允许对表示日期的字符串进行格式化和句法分析。在JDK1.1中提供了类Calendar来完成第一种功能,类DateFormat来完成第二项功能。dateFormat是java.text包中的一个类。与Date类有所不同的是,DateFormat类接受用各种语言和不同习惯表示的日期字符串。本节将介绍java.util包中的类Calendar及其它新增加的相关的类。 类Calendar是一个抽象类,它完成日期(Date)类和普通日期表示法(即用一组整型域如YEAR,MONTH,DAY,HOUR表示日期)之间的转换。 由于所使用的规则不同,不同的日历系统对同一个日期的解释有所不同。在JDK1.1中提供了Calendar类一个子类GregorianCalendar??它实现了世界上普遍使用的公历系统。当然用户也可以通过继承Calendar类,并增加所需规则,以实现不同的日历系统。 第GregorianCalendar继承了Calendar类。本节将在介绍类GregorianCalendar的同时顺带介绍Calendar类中的相关方法。 类GregorianCalendar提供了七种构造函数: (1)public GregorianCalendar() 创建的对象中的相关值被设置成指定时区,缺省地点的当前时间,即程序运行时所处的时区、地点的当前时间。 (2)public GregorianCalendar(TimeZone zone) 创建的对象中的相关值被设置成指定时区zone,缺省地点的当前时间。 (3)public GregorianCalendar(Locale aLocale) 创建的对象中的相关值被设置成缺省时区,指定地点aLocale的当前时间。 (4)public GregorianCalendar(TimeZone zone,Local aLocale) 创建的对象中的相关值被设置成指定时区,指定地点的当前时间。 上面使用到的类TimeZone的性质如下: TimeZone是java.util包中的一个类,其中封装了有关时区的信息。每一个时区对应一组ID。类TimeZone提供了一些方法完成时区与对应ID两者之间的转换。 (Ⅰ)已知某个特定的ID,可以调用方法 public static synchronized TimeZone getTimeZone(String ID)来获取对应的时区对象。 例 太平洋时区的ID为PST,用下面的方法可获取对应于太平洋时区的时区对象: TimeZone tz=TimeZone.getTimeZone("PST"); 调用方法getDefault()可以获取主机所处时区的对象。 TimeZone tz=TimeZone.getDefault(); (Ⅱ)调用以下方法可以获取时区的ID ■public static synchronized String[] getavailableIDs(int rawOffset) 根据给定时区偏移值获取ID数组。同一时区的不同地区的ID可能不同,这是由于不同地区对是否实施夏时制意见不统一而造成的。 例String s[]=TimeZone.getAvailableIDs(-7*60*60*1000); 打印s,结果为s[0]=PNT,s[1]=MST ■public static synchronized String[] getAvailableIDs() 获取提供的所有支持的ID。 ■public String getID() 获取特定时区对象的ID。 例 TimeZone tz=TimeZone.getDefault(); String s=tz.getID(); 打印s,结果为s=CTT。 上面使用类的对象代表了一个特定的地理、政治或文化区域。Locale只是一种机制,它用来标识一类对象,Local本身并不包含此类对象。 要获取一个Locale的对象有两种方法: (Ⅰ)调用Locale类的构造方法 Locale(String language,String country) Locale(String language,String country,String variant) 参数说明:language??在ISO-639中定义的代码,由两个小写字母组成。 country??在ISO-3166中定义的代码,由两个大写字母组成。 variant??售货商以及特定浏览器的代码,例如使用WIN代表Windows。 (Ⅱ)调用Locale类中定义的常量 Local类提供了大量的常量供用户创建Locale对象。 例 Locale.CHINA 为中国创建一个Locale的对象。 类TimeZone和类Locale中的其它方法,读者可查阅API。 (5)public GregorianCalendar(int year,int month,int date) (6)public GregorianCalendar(int year,int month,int date,int hour,int minute) (7)public GregorianCalendar(int year,int month,int date,int hour,int minute,int second) 用给定的日期和时间创建一个GregorianCalendar的对象。 参数说明: year-设定日历对象的变量YEAR;month-设定日历对象的变量MONTH; date-设定日历对象的变量DATE;hour-设定日历对象的变量HOUR_OF_DAY; minute-设定日历对象的变量MINUTE;second-设定日历对象的变量SECOND。 与Date类中不同的是year的值没有1900这个下限,而且year的值代表实际的年份。month的含义与Date类相同,0代表1月,11代表12月。 例 GregorianCalendar cal=new GregorianCalendar(1991,2,4) cal的日期为1991年3月4号。 除了与Date中类似的方法外,Calendar类还提供了有关方法对日历进行滚动计算和数学计算。计算规则由给定的日历系统决定。进行日期计算时,有时会遇到信息不足或信息不实等特殊情况。Calendar采取了相应的方法解决这些问题。当信息不足时将采用缺省设置,在GregorianCalendar类中缺省设置一般为YEAR=1970,MONTH=JANUARY,DATE=1。 当信息不实时,Calendar将按下面的次序优先选择相应的Calendar的变量组合,并将其它有冲突的信息丢弃。 MONTH+DAY_OF_MONTH MONTH+WEEK_OF_MONTH+DAY_OF_WEEK MONTH+DAY_OF_WEEK_OF_MONTH+DAY_OF_WEEK DAY_OF+YEAR DAY_OF_WEEK_WEEK_OF_YEAR HOUR_OF_DAY8.4 随机数类Random Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处,后者只产生double型的随机数。 类Random中的方法十分简单,它只有两个构造方法和六个普通方法。 构造方法: (1)public Random() (2)public Random(long seed) Java产生随机数需要有一个基值seed,在第一种方法中基值缺省,则将系统时间作为seed。 普通方法: (1)public synonronized void setSeed(long seed) 该方法是设定基值seed。 (2)public int nextInt() 该方法是产生一个整型随机数。 (3)public long nextLong() 该方法是产生一个long型随机数。 (4)public float nextFloat() 该方法是产生一个Float型随机数。 (5)public double nextDouble() 该方法是产生一个Double型随机数。 (6)public synchronized double nextGoussian() 该方法是产生一个double型的Goussian随机数。 例8.2 RandomApp.java。 //import java.lang.*; import java.util.Random; public class RandomApp{ public static void main(String args[]){ Random ran1=new Random(); Random ran2=new Random(12345); //创建了两个类Random的对象。 System.out.println("The 1st set of random numbers:"); System.out.println("/t Integer:"+ran1.nextInt()); System.out.println("/t Long:"+ran1.nextLong()); System.out.println("/t Float:"+ran1.nextFloat()); System.out.println("/t Double:"+ran1.nextDouble()); System.out.println("/t Gaussian:"+ran1.nextGaussian()); //产生各种类型的随机数 System.out.print("The 2nd set of random numbers:"); for(int i=0;i public static void main(String[] args){ Vector v1=new Vector(); Integer integer1=new Integer(1); v1.addElement("one"); //加入的为字符串对象 v1.addElement(integer1); v1.addElement(integer1); //加入的为Integer的对象 v1.addElement("two"); v1.addElement(new Integer(2)); v1.addElement(integer1); v1.addElement(integer1); System.out.println("The vector v1 is:/n/t"+v1); //将v1转换成字符串并打印 v1.insertElementAt("three",2); v1.insertElementAt(new Float(3.9),3); System.out.println("The vector v1(used method insertElementAt()) is:/n/t "+v1); //往指定位置插入新的对象,指定位置后的对象依次往后顺延 v1.setElementAt("four",2); System.out.println("The vector v1(used method setElementAt()) is:/n/t "+v1); //将指定位置的对象设置为新的对象 v1.removeElement(integer1); //从向量对象v1中删除对象integer1由于存在多个integer1所以从头开始 //找,删除找到的第一个integer1 Enumeration enum=v1.elements(); System.out.print("The vector v1(used method removeElement())is:"); while(enum.hasMoreElements()) System.out.print(enum.nextElement()+" "); System.out.println(); //使用枚举类(Enumeration)的方法来获取向量对象的每个元素 System.out.println("The position of object 1(top-to-bottom):" + v1.indexOf(integer1)); System.out.println("The position of object 1(tottom-to-top):" +v1.lastIndexOf(integer1)); //按不同的方向查找对象integer1所处的位置 v1.setSize(4); System.out.println("The new vector(resized the vector)is:"+v1); //重新设置v1的大小,多余的元素被行弃 } } 运行结果: E:/java01>java VectorApp The vector v1 is: [one, 1, 1, two, 2, 1, 1] The vector v1(used method insertElementAt()) is: [one, 1, three, 3.9, 1, two, 2, 1, 1] The vector v1(used method setElementAt()) is: [one, 1, four, 3.9, 1, two, 2, 1, 1] The vector v1(used method removeElement())is:one four 3.9 1 two 2 1 1 The position of object 1(top-to-bottom):3 The position of object 1(tottom-to-top):7 The new vector(resized the vector)is:[one, four, 3.9, 1] E:/java01> 从例8.3运行的结果中可以清楚地了解上面各种方法的作用,另外还有几点需解释。 (1)类Vector定义了方法 public final int size() 此方法用于获取向量元素的个数。它的返回值是向是中实际存在的元素个数,而非向量容量。可以调用方法capactly()来获取容量值。 方法: public final synchronized void setsize(int newsize) 此方法用来定义向量大小。若向量对象现有成员个数已超过了newsize的值,则超过部分的多余元素会丢失。 (2)程序中定义了Enumeration类的一个对象 Enumeration是java.util中的一个接口类,在Enumeration中封装了有关枚举数据集合的方法。 在Enumeration中提供了方法hawMoreElement()来判断集合中是束还有其它元素和方法nextElement()来获取下一个元素。利用这两个方法可以依次获得集合中元素。 Vector中提供方法: public final synchronized Enumeration elements() 此方法将向量对象对应到一个枚举类型。java.util包中的其它类中也大都有这类方法,以便于用户获取对应的枚举类型。8.6 栈类Stack Stack类是Vector类的子类。它向用户提供了堆栈这种高级的数据结构。栈的基本特性就是先进后出。即先放入栈中的元素将后被推出。Stack类中提供了相应方法完成栈的有关操作。 基本方法: public Object push(Object Hem) 将Hem压入栈中,Hem可以是任何类的对象。 public Object pop() 弹出一个对象。 public Object peek() 返回栈顶元素,但不弹出此元素。 public int search(Object obj) 搜索对象obj,返回它所处的位置。 public boolean empty() 判别栈是否为空。 例8.4 StackApp.java使用了上面的各种方法。 例8.4 StackApp.java。 import java.lang.*; import java.util.*; public class StackApp{ public static void main(String args[]){ Stack sta=new Stack(); sta.push("Apple"); sta.push("banana"); sta.push("Cherry"); //压入的为字符串对象 sta.push(new Integer(2)); //压入的为Integer的对象,值为2 sta.push(new Float(3.5)); //压入的为Float的对象,值为3.5 System.out.println("The stack is,"+sta); //对应栈sta System.out.println("The top of stack is:"+sta.peek()); //对应栈顶元素,但不将此元素弹出 System.out.println("The position of object Cherry is:" +sta.search("cherry")); //打印对象Cherry所处的位置 System.out.print("Pop the element of the stack:"); while(!sta.empty()) System.out.print(sta.pop()+" "); System.out.println(); //将栈中的元素依次弹出并打印。与第一次打印的sta的结果比较,可看出栈 //先进后出的特点 } } 运行结果(略)8.7 哈希表类Hashtable 哈希表是一种重要的存储方式,也是一种常见的检索方法。其基本思想是将关系码的值作为自变量,通过一定的函数关系计算出对应的函数值,把这个数值解释为结点的存储地址,将结点存入计算得到存储地址所对应的存储单元。检索时采用检索关键码的方法。现在哈希表有一套完整的算法来进行插入、删除和解决冲突。在Java中哈希表用于存储对象,实现快速检索。 Java.util.Hashtable提供了种方法让用户使用哈希表,而不需要考虑其哈希表真正如何工作。 哈希表类中提供了三种构造方法,分别是: public Hashtable() public Hashtable(int initialcapacity) public Hashtable(int initialCapacity,float loadFactor) 参数initialCapacity是Hashtable的初始容量,它的值应大于0。loadFactor又称装载因子,是一个0.0到0.1之间的float型的浮点数。它是一个百分比,表明了哈希表何时需要扩充,例如,有一哈希表,容量为100,而装载因子为0.9,那么当哈希表90%的容量已被使用时,此哈希表会自动扩充成一个更大的哈希表。如果用户不赋这些参数,系统会自动进行处理,而不需要用户操心。 Hashtable提供了基本的插入、检索等方法。 ■插入 public synchronized void put(Object key,Object value)给对象value设定一关键字key,并将其加到Hashtable中。若此关键字已经存在,则将此关键字对应的旧对象更新为新的对象Value。这表明在哈希表中相同的关键字不可能对应不同的对象(从哈希表的基本思想来看,这也是显而易见的)。 ■检索 public synchronized Object get(Object key) 根据给定关键字key获取相对应的对象。 public synchronized boolean containsKey(Object key) 判断哈希表中是否包含关键字key。 public synchronized boolean contains(Object value) 判断value是否是哈希表中的一个元素。 ■删除 public synchronized object remove(object key) 从哈希表中删除关键字key所对应的对象。 public synchronized void clear() 清除哈希表 另外,Hashtalbe还提供方法获取相对应的枚举集合: public synchronized Enumeration keys() 返回关键字对应的枚举对象。 public synchronized Enumeration elements() 返回元素对应的枚举对象。 例8.5 Hashtable.java给出了使用Hashtable的例子。 例8.5 Hashtalbe.java。 //import java.lang.*; import java.util.Hashtable; import java.util.Enumeration; public class HashApp{ public static void main(String args[]){ Hashtable hash=new Hashtable(2,(float)0.8); //创建了一个哈希表的对象hash,初始容量为2,装载因子为0.8 hash.put("Jiangsu","Nanjing"); //将字符串对象"Jiangsu"给定一关键字"Nanjing",并将它加入hash hash.put("Beijing","Beijing"); hash.put("Zhejiang","Hangzhou"); System.out.println("The hashtable hash1 is: "+hash); System.out.println("The size of this hash table is "+hash.size()); //打印hash的内容和大小 Enumeration enum1=hash.elements(); System.out.print("The element of hash is: "); while(enum1.hasMoreElements()) System.out.print(enum1.nextElement()+" "); System.out.println(); //依次打印hash中的内容 if(hash.containsKey("Jiangsu")) System.out.println("The capatial of Jiangsu is "+hash.get("Jiangsu")); hash.remove("Beijing"); //删除关键字Beijing对应对象 System.out.println("The hashtable hash2 is: "+hash); System.out.println("The size of this hash table is "+hash.size()); } } 运行结果: The hashtable hash1 is: {Beijing=Beijing, Zhejiang=Hangzhou, Jiangsu=Nanjing} The size of this hash table is 3 The element of hash is: Beijing Hangzhou Nanjing The capatial of Jiangsu is Nanjing The hashtable hash2 is: {Zhejiang=Hangzhou, Jiangsu=Nanjing} The size of this hash table is 2 Hashtable是Dictionary(字典)类的子类。在字典类中就把关键字对应到数据值。字典类是一个抽象类。在java.util中还有一个类Properties,它是Hashtable的子类。用它可以进行与对象属性相关的操作。8.8 位集合类BitSet 位集合类中封装了有关一组二进制数据的操作。 我们先来看一下例8.6 BitSetApp.java。 例8.6 BitSetApp.java //import java.lang.*; import java.util.BitSet; public class BitSetApp{ private static int n=5; public static void main(String[] args){ BitSet set1=new BitSet(n); for(int i=0;i Object obj = it.next(); // 得到下一个元素 } 由Collection接口派生的两个接口是List和Set。 List接口 List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素。 除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。 实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。 LinkedList类 LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。 注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List: List list = Collections.synchronizedList(new LinkedList(...)); ArrayList类 ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。 每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。 和LinkedList一样,ArrayList也是非同步的(unsynchronized)。 Vector类 Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。 Stack 类 Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。 Set接口 Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。 很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。 请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。 Map接口 请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。 Hashtable类 Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。 添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”: Hashtable numbers = new Hashtable(); numbers.put(“one”, new Integer(1)); numbers.put(“two”, new Integer(2)); numbers.put(“three”, new Integer(3)); 要取出一个数,比如2,用相应的key: Integer n = (Integer)numbers.get(“two”); System.out.println(“two = ” + n); 由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。 如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。 Hashtable是同步的。 HashMap类 HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。 WeakHashMap类 WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。 总结 如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
|
CopyRight 2018-2019 实验室设备网 版权所有 |