第13章 集合
__13_1 集合接口
1)将集合的接口和实现分离
一个队列接口的最小形式可能是下面这样
interface Queue<E> // a simplified form of the interface in the standard library
{
void add(E element);
E remove();
int size();
}
可以用循环数组或者链表实现队列。而当在程序中使用队列时,一旦构建了集合就不需要知道接口究竟使用了哪种实现,可以使用接口类型存放集合的引用。
Queue<Customer> expressLane = new CircularArrayQueue<Customer>(100);
expressLane.add(new Customer("Harry"));
一旦改变了想法,可以轻松地使用另外一种不同的实现。
2) Java类库中的集合接口和迭代器接口
集合类的基本接口使Collection接口,这个接口有两个基本方法:
public interface Collection<E>
{
boolean add(E element);
Iterator<E> iterator();
...
}
add方法用于向集合中添加元素。如果添加元素确实改变了集合就返回true。
iterator方法用于返回一个实现了Iterator接口的对象。
1.迭代器
Iterator接口包含三个方法:
public interface Iterator<E>
{
E next();
boolean hasNext();
void remove();
}
如果到达了集合的末尾,next方法将抛出一个NoSuchElementException。因此,需要在调用next之前调用hasNext方法。
从Java SE 5.0起,for each循环可以用来和任何实现了Iterable接口的对象一起工作,这个接口只包含一个方法:
public interface Iterable<E>
{
Iterator<E> iterator(); //返回实现了Iterator接口的类的对象
}
Collection 接口扩展了Iterable接口,因此,对于标准类库中的任何集合都可以使用for each循环。
可以将Iterator.next与InputStream.read看作等效的。从数据流中读取一个字节,就会自动的“消耗掉”这个字节。下一次调用read将会消耗并返回输入的下一个字节。
2.删除元素
Iterator接口的remove方法将会删除上次调用next方法时返回的元素,想要删除指定位置的元素,仍需要越过这个元素。
Iterator<String> it = c.iterator();
it.next(); // skip over the first element
it.remove(); //now rm it
如果调用remove之前没有调用next将是不合法的,会抛出一个IllegalStateException异常。
3.泛型实用方法
如果实现Collection接口的每一个类都要提供所有的例行方法使一件烦人的事,为了能让实现者更容易实现这个接口,Java类库提供了一个类AbstractCollection,它将基础方法size和iterator抽象化了,但是提供了例行方法,如contains等。
__13_2 具体的集合
1)链表
List<String> staff = new LinkedList<String>(); // LinkedList implements List
staff.add("Amy");
staff.add("Bob");
staff.add("Carl");
Iterator iter = staff.iterator();
String first = iter.next();
String second = iter.next();
iter.remove();
链表和泛型集合之间有一个重要区别,即链表是有序集合。LinkedList.add方法把元素添加到链表尾部。当需要把元素添加到链表中间时,这种依赖位置的add方法将由迭代器负责。只有对自然有序的集合使用迭代器添加元素才有实际意义。而集(set)类型中,元素完全无序,因此,在Iterator接口中就没有add方法。
集合类库提供了ListIterator接口,其中包含add方法
interface ListIterator<E> extends Iteartor<E>
{
void add(E element);
...
}
ListIterator有两个可以用来反向遍历链表的方法,
E previous()
boolean hasPrevious()
LinkedList类的listIterator方法返回了一个实现了ListIterator接口的迭代器对象。调用迭代器的add方法会把元素插入至迭代器当前位置之前。
List<String> staff = new LinkedList<String>(); // LinkedList implements List
staff.add("Amy");
staff.add("Bob");
staff.add("Carl");
Iterator iter = staff.listIterator();
iter.next();
iter.add("Juliet");// skip past first element
iter.remove();
在调用next后,remove会删除迭代器左侧元素,而调用previous就会将右侧元素删除。
set方法用一个新元素取代next/previous方法返回的上一个元素。
如果在某个迭代器修改集合时,另一个迭代器对其进行遍历,一定会出现混乱的状况。如果迭代器发现它的集合被另一个迭代器修改了,或是被该集合的自身方法修改了,就会抛出一个ConcurrentModificationException异常。(一个简单的实现方法:集合跟踪改写操作的次数,每个迭代器也维护一个独立的计数值,在每个迭代器方法的开始处检查自己改写操作的计数值是否与集合的改写操作计数值一致。如果不一致,抛出一个ConcurrentModificationException异常)例如:
List<String> list = ....;
ListIterator<String> iter1 = list.listIterator();
ListIteartor<String> iter2 = list.listIterator();
iter1.next();
iter1.remove();
iter2.next();//throws ConcurrentModificationException
书中建议,可以根据需要给容器附加许多的迭代器,但是这些迭代器只能读取列表。另外,再附加一个既能读又能写的迭代器。(???下述例子,只有li2可以读写,仍然有异常???)
LinkedList<String> ll = new LinkedList<String>();
ll.add("Joseph");
ll.add("Pig");
ll.add("Alice");
ll.add("Bob");
ll.add("Carl");
ListIterator li1 = ll.listIterator();
ListIterator li2 = ll.listIterator();
li2.next();
li2.add("lala");
li1.next();
li1.next();
System.out.println(ll);
另外,在使用Iterator遍历的过程中删除元素时,在调用惹remove方法后会跳出while(iter.hasNext)的循环。
LinkedList<String> ll = new LinkedList<String>();
ll.add("Joseph");
ll.add("Pig");
ll.add("Alice");
ll.add("Bob");
ll.add("Carl");
ListIterator<String> li1 = ll.listIterator();
while(li1.hasNext())
{
String str = li1.next();
if(str.equals("Bob"))
li1.remove();//在移除Bob后,将跳出while循环,即不会打印Carl
else
System.out.println(str);
}
链表只负责跟踪结构性改写(如添加删除等),而不把set作为结构性修改。
LinkedList提供了一个用来访问某个特定元素的get方法:
LinkedList<String> list = ...;
String obj = list.get(n);// 如果索引大于 size()/2 就从列表尾端搜索
如果有一个整数索引n,list.listIterator(n)将返回一个迭代器,这个迭代器指向索引为n的元素前面的位置。
调用System.out.println(a),通过调用AbstractCollection类中的toString方法打印除链表a中的所有元素。
使用 |ACE A|CE AB|CE这样的方式注意迭代器的位置会有帮助。
2)数组列表
List接口用于描述一个有序集合。Vector类的所有方法都是同步的。可以由两个线程安全的访问一个Vector对象。建议在不需要同步时使用ArrayList而不适用Vector
3)散列集
自己实现的hashCode方法应该与equals方法兼容,即若a.equals(b)为true,a与b必须具有相同的散列码。散列码可以是任何整数,包括整数或负数。
在Java中,散列表用链表数组实现,每个列表称为桶。计算对象的散列码并对桶数取余,得到的结果就是保存这个对象的桶的索引。
如果想要更多地控制散列表的性能,就要指定一个初始的桶数。通常,将桶数设置为预计元素个数的75%~150%。有些研究人员认为:尽管还没有确凿的证据,但最好将桶数设置为素数,以防键的集聚。
如果散列表太慢,就需要再散列(rehashed)。如果要对散列表再散列,就需要创建一个桶数更多的表,并将所有元素插入新表中,废弃旧表。装填因子(load factor)决定何时对散列表进行再散列。例如,如果装填因子为0.75(默认值),而表中超过75%的位置已经填入元素,这个表就会用双倍的桶数自动地进行再散列。
Java集合类库提供了一个HashSet类,它实现了基于散列表的集。
Set<String> words = new HashSet<String(); // HashSet implements Set
words.add("Oh");
4)树集
树集是一个有序集合。
当前使用的是红黑树。
5)对象的比较
TreeSet假定插入元素实现了Comparable接口。
public interface Comparable<T>
{
int compareTo(T other);
}
a.compareTo(b),在a,b相等时返回0;如果排序后a位于b之前,则返回负值;如果a位于b之后,则返回正值。
然而这样的实现方式有其局限性,没有办法解决需要两种排序方式的需求(根据产品编号排序,根据产品类别排序)。Comparator接口声明了一个带有两个显示参数的compare方法:
public interface Comparator<T>
{
int compare(T a, T b);
}
如果按照描述信息进行排序,就直接定义一个实现Comparator接口的类:
class ItemComparator implements Comparator<Item>
{
public int compare(Item a, Item b)
{
...
}
}
然后将这个类的对象传递给树集的构造器:
//Plan A
ItemCompoarator comp = new ItemComparator();
SortedSet<Item> sortByDescription = new TreeSet<Item>(comp);
//Plan B
SortedSet<Item> sortByDescription = new TreeSet<Item>( new Comparator<Item>(){
public int compare(Item a, Item b)
{
...
}
});
compara方法的返回结果为0则代表两对象相等。
6)队列与双端队列
Java SE 6中引入了Deque接口,并由ArrayDeque和LinkedList类实现。
Queue及Deque中搞得offer*方法表示测试能否在队列中加入元素,不能时返回false。而直接使用add*方法插入,若队列已满则抛出异常。
remove*和poll*方法删除并返回队列一端的元素。前者队列为空时抛出异常,后者返回null。
get*和peek*方法获取队列一段的元素,队列为空时前者抛出异常,后者返回null。
7)优先级队列(priority queue)
无论何时调用remove方法,总会获得当前的优先级队列中最小的元素。优先级队列使用了堆。
优先级队列的典型示例是任务调度。每一个任务有一个优先级,将优先级最高的任务从队列中删除,由于习惯上将1设为“最高”优先级,所以会将最小的元素删除)。
与TreeSet迭代不同,这里的迭代并不是按照元素的排列顺序迭代的。删除时总是删掉剩余元素中优先级数最小的元素。
8)映射表
集是一个集合,但是要查看一个元素就需要有查找元素的精确副本。
通常,我们知道某些键的信息,并需要查找与之对应的元素。映射表(map)数据结构就是为此设计的。映射表用来存放键/值对。
Java类库为映射表提供了两个通用的实现:HashMap和TreeMap。这两个类都实现了Map接口。
下列代码为存储的员工信息建立一个散列映射表:
Map<String, Employee> staff = new HashMap<String, Employee>();//HashMap implements Map
Employee harry = new Employee("Harry Hacker");
staff.put("987-98-9996", harry);
想要检索一个对象,必须提供一个键:
String s = "987-98-9996";
e = staff.get(s); //gets harry
如果在映射表中没有与给定键对应的信息,get将返回null。
键必须是唯一的,对同一个键两次调用put方法,第二个值会取代第一个值,put将返回用这个键参数存储的上一个值。
remove方法用于从映射表中删除给定键对应的元素。
siez方法用于返回映射表中的元素数。
可以获得映射表的三种视图
- Set< K> keySet()
- Collection< V> values()
- Set< Map.Entry< K, V>> entrySet()
keySet不是HashSet也不是TreeSet而是实现了Set接口的另一个类的对象。Set接口扩展了Collection接口。
如果想要同时查看键与值,就可以通过枚举各个条目查看:
for(Map.Entry<String, Employee> entry : staff.entrySet())
{
String key = entry.getKey();
Employee value = entry.getValue();
}
如果使用迭代器的remove方法,实际上就从映射表中删除了键及对应的值,但是,不能将元素添加到键集的视图中(无意义)。如果试图调用add方法,会抛出一个UnsupportedOperationException异常。虽然添加新键值对是有意义的,但是条目集视图也有同样的限制。
putAll方法将给定映射表中的所有条目添加到这个映射表中。
SortedMap中有firstKey和lastKey方法,返回映射表中最小元素和最大元素。
9)专用集与映射表类
1.弱散列映射表
需要由程序负责从长期存货的映射表中删除那些无用的值。或者使用WeakHashMap来完成这件事。当对键的唯一引用来自散列表条目时,这一数据结构将与垃圾回收器协同工作一起删除键/值对。
2.链接散列集和链接映射表
Java SE 1.4增加了两个类:LinkedHashSet和LinkedHashMap,用来记住插入元素项的顺序当条目插入到表中时,就会并入到双向链表中。
映射表(Map)的iterator将按照插入顺序遍历。
可以通过LinkedHashMap< K, V>(initialCapacity, loadFactor, true)
来构造一个用访问顺序,而不是插入顺序对映射表条目迭代的链接散列映射表。每次调用get或put,受到影响的条目会从当前位置移动到条目链表的尾部,而桶不受影响。这样的访问顺序可以实现高速缓存的最近最少使用原则。例如,可能希望将访问频率高的元素放在内存中,而访问频率低的元素从数据库中读取。当表中找不到元素项,且表已满时,可以将迭代器加入到表中,并将枚举的前几个元素删除掉。
可以通过构造一个LinkedHashMap的子类,然后覆盖
protected boolean removeEldestEntry(Map.Entry< K, V> eldest)
每当方法返回true时,就添加一个新条目,删除eldest条目。
Map< K, V> cache = new LinkedHashMap< K, V>(128, 0.75F, true)
{
protected boolean removeEldestEntry(Map.Entry< K, V> eldest)
{
return size() > 100;
}
};
3.枚举集与枚举映射表
EnumSet使一个枚举类型元素集的高效实现。由于枚举类型只有有限个实例,所以EnumSet内部用位序列实现。如果对应的值在集中,则相应的位被置为1。
EnumSet类没有公共的构造器,可以使用静态工厂方法构造这个集:
enum Weekday{MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
EnumSet<Weekday> always = EnumSet.allOf(Weekday.class);
EnumSet<Weekday> never = EnumSet.noneOf(Weekday.class);
EnumSet<Weekday> workday = EnumSet.range(Weekday.MONDAY, Weekday.FRIDAY);
EnumSet<Weekday> mwf = EnumSet.of(Weekday.MONDAY, Weekday.WEDNESDAY, Weekday.FRIDAY);
可以使用Set接口的常用方法来修改EnumSet。
EnumMap是一个键类型为枚举类型的映射表。它可以直接且高效的用一个值数组实现。在使用时,需要在构造器中指定键类型:
EnumMap<Weekday, Employee> personInCharge = new EnumMap<Weekday, Employee>(Weekday.class);
4.标识散列映射表
IdentityHashMap类中,键的散列值不是用hashCode计算的,而是用System.identityHashCode方法计算的。这是Object.hashCode方法根据对象的内存地址来计算散列码的方式。而且,在对两个对象进行比较时,IdentityHashMap类用==,而不使用equals。
也就是说,不同键的对象,即使内容相同,也被视为是不同的对象。
__13_3 集合框架
框架(framework)是一个类的集。
下图为集合框架的接口图
RandomAccess是一个标记接口,这个接口没有任何方法,但是可以用来检测一个特定的集合是否支持高效的随机访问,ArrayList和Vector类都实现了RandomAccess接口。
可以用这个标记接口检测一个特定的集合是否支持高效的随机访问:
if( c instanceof RandomAccess)
{
//use random access algorithm
}
else
{
//use sequential access algorithm
}
ListIterator接口定义了一个方法:void add (E element)
Set接口与Collection接口是一样的,只是其方法的行为有着更加严谨的定义。集的add方法拒绝添加重复的元素。集的equals方法定义两个集相等的条件是它们包含相同的元素但顺序不必相同。
并不是所有集合都是集,建立Set接口后可以让程序员编写仅接受集的方法。
NavigableSet和NavigableMap包含了几个用于在有序集和映射表中查找和遍历的方法。TreeSet和TreeMap类实现了这几个接口。
集合框架中的类如下图所示:
1)视图与包装器
keySet方法返回一个实现Set接口的类对象,这个类的方法对原映射表进行操作,这种集合称为视图。
1.轻量级集包装器
Arrays类的静态方法asList将返回一个包装了普通Java数组的List包装器。它是一个 视图对象,带有访问底层数组的get和set方法。改变数组大小的所有方法(如add和remove)都会抛出一个UnsupportedOperationException异常。
List<String> list = Arrays.asList("Joseh", "Pig", "Alice", "Bob");
System.out.println("type = " + list.getClass().getName()); //output "java.util.Arrays$ArrayList"
list.add("And"); //UnsupportedOperationException
Arrays$ArrayList类继承了AbstractList类,而该类的add方法永远将抛出UnsupportedOperationException异常。
Collections.nCopies(n, anObject)将返回一个实现了List 接口的不可修改的对象。创建长度为n,每个元素都是anObject的列表。而返回的CopiesList对象的实例域只有n和element两项,其值分别为n和anObject。
List<String> settings = Collections.nCopies(100, "DEFAULT");
Collections类包含了许多实用方法。
Collections.singleon(anObject)
将返回一个视图对象。这个对象实现了Set接口。返回对象为一个不可修改的长度为1的单元素集。
2.子范围
可以为很多集合建立自范围(subrange)视图。例如,假设有一个列表staff,想从中取出第10~19个元素。可以使用subList方法获得一个列表的子范围视图。
List group2 = staff.subList(10, 20);
//第一个索引包含在内,第二个索引不包含在内,与String.substring参数情况一样
可以将任何操作应用于子范围,并能够自动地反应整个列表的情况。可以对子集添加,删除,设置元素。而对Arrays.asList方法建立的List的子集,只可以读写元素,仍然不可以改变表的结构。
对于有序集和映射表,可以使用排序顺序而不是元素位置建立子范围。SortedSet接口声明了3个方法:
SortedSet<E> subSet(E from, E to)
SortedSet<E> headSet(E to)
SortedSet<E> tailSet(E from)
有序映射表也有类似的方法:
SortedMap<K, V> subMap(K from, k to)
SortedMap<K, V> headMap(K to)
SortedMap<K, V> tailMap(K from)
Java SE 6引入的NavigableSet接口赋予了自范围操作更多的控制能力,可以制定是否包括边界:
NavigableSet<E> subSet(E from, boolean fromInclusive, E to, boolean toInclusive)
NavigableSet<E> headSet(E to, boolean toInclusive)
NavigableSet<E> tailSet(E from, boolean fromInclusive)
3.不可修改的视图
Collections还有几个方法,用于产生集合的不可修改视图(unmodifiable views)。这些视图对现有集合增加了一个运行时的检查,如果发现试图对集合进行修改,就抛出一个异常,同时这个集合将保持未修改的状态。
Collections.unmodifiableCollection
Collections.unmodifiableList
Collections.unmodifiableSet
Collections.unmodifiableSortedSet
Collections.unmodifiableMap
Collections.unmodifiableSortedMap
每个方法定义于一种接口,可以和任何实现了该接口的其他类一起协同工作。
List<STring> staff = new LinkedList<String();
...
lookAt(new Collections.unmodifiableList(staff);
这些方法返回的对象仍可以调用对应接口的所有方法,但是所有的更改器方法(例如,add)已经被重新定义为抛出一个UnsupportedOperationException异常,而不是将调用传递给底层集合。
unmodifiableCollection方法将返回一个集合,它的equals方法不调用底层集合的equals方法。相反,它继承了Object类的equals方法,这个方法只是检测两个对象是否是同一个对象。视图将以同样的方法处理hashCode方法。
然而,unmodifiableSet类和unmodifiableList类却使用底层集合的equals方法和hashCode方法。
4.同步视图
如果由多线程访问集合,就必须确保集不会被意外的破坏。例如,如果一个线程试图将元素添加到散列表中,同时另一个线程正在对散列表再散列,其结果将是灾难性的。
Collections类的静态synchronizedMap方法可以将任何一个映射表转换成具有同步访问方法的Map:
Map<String, Employee> map = Collections.synchronizedMap(new HashMap<String, Employee>());
现在就可以由多线程访问map对象了,像get和put这类方法都是串行操作的,即在另一个线程调用另一个方法之前,刚才的方法调用必须彻底完成。
5.被检验视图
Java SE 5.0增加了一组“被检验”视图,用来对泛型类型发生问题时提供调试支持。实际上,将错误类型的元素私自带到泛型集合中的问题极有可能发生。例如:
ArrayList<String> strings = new ArrayList<String>();
ArrayList rawList = strings;// get warning only, not an error, for compatibility with legacy code
rawList.add(new Date()); //now strings contains a Date object!
这个错误的add命令在运行时检测不到。相反,只有在稍后的另一部分代码中调用get方法,并将结果转化为String时,这个类才会抛出异常。
被检验视图可以探测到这类问题。下面定义了一个安全列表:
List<String> safeStrings = Collections.checkedList(strings, String.class);
ArrayList rawList = safeStrings;
rawList.add(new Date()); // Checked list throws a ClassCastException
视图的add方法将检测插入的对象是否属于给定的类。但是,被检测视图受限于虚拟机可以运行的运行时检查。例如,对于ArrayList< Pair< String>>,由于虚拟机有一个单独的“原始”Pair类,所以,无法组织插入Pair< Date>
6.关于可选操作的说明
在集合和迭代器接口的API文档中,许多方法描述为“可选操作”。这看起来与接口的概念有所抵触。是集合类库的设计者必须解决一组特别严格且又相互冲突的需求的结果。用户希望类库易于学习,使用方便,彻底泛型化,面向通用性,同时又与手写算法一样高效。要同时达到所有目标的要求,或者尽量兼顾所有目标完全是不可能的。
2)批操作
批操作可以避免频繁地使用迭代器。
1. 找出两个集合的交
Set<String> result = new HashSet<String>(a);
result.retainAll(b);
2. 删除映射表中对应主键的条目
Map<String, Employee> staffMap = ...;
Set<String> delIDs = ...;
staffMap.KeySet().removeAll(delIDs);
3.将一个列表的前十个元素添加到另一个容器中
relocated.addAll(staff.subList(0,10));
4.删除表中前十个元素
staff.subList(0,10).clear();
3)集合和数组之间的转换
1.数组转集合
String[] arr = ...;
HashSet<String> staff = new HashSet<String>(Arrays.asList(arr));
2.集合转数组
toArray方法返回的数组是一个Object[]数组,无法改变其类型。
使用另一种toArray方法:
String[] values = staff.toArray(new String[0]);
staff.toArray(new String[staff.size()]);
__13_4 算法
泛型集合接口有一个很大的优点,即算法只需要实现一次。
1)排序与混排
Collections类中的sort方法可以对实现了List接口的集合进行排序。
List<STring> staff = new LinkedList<String>();
//fill collection
Collections.sort(staff);
如果想用其他方式对列表进行排序,可以将Comparator对象作为第二个参数传递给sort方法。
Comparator<Item> itemComparator = new Comparator<Item>()
{
public int compare(Item a, Item b)
{
return a.partNumber - b.partNumber;
}
};
Collections.sort(items, itemComparator);
如果想按照降序对列表进行排序,可以使用Collections.reverseOrder()方法,这个方法返回一个比较器,比较器返回b.compareTo(a):
Collections.sort(staff, Collections.reversOrder())
根据元素类型的compareTo方法的逆序进行排序:
Collections.sort(items, Collections.reverseOrder(itemComparator));
可以使用归并算法对列表进行高效的排序,Java语言将所有元素转入一个数组,并使用一种归并排序的变体对数组进行排序,然后再将排序后的序列复制回列表。
使用归并而非快排的原因是,稳定。已经按照姓名排列的员工列表,再按照工资排序,将不会破坏工资相同的员工的排列顺序。
术语定义:
- 如果列表支持add和remove方法,则是可改变大小的
- 如果列表支持set方法,则是可修改的
Collections类有一个shuffle算法,随机地混排列表中元素的顺序。例如:
ArrayList<Card> cards = ...;
Collectionns.shuffle(cards);
2)二分查找
Collections类的binarySearch实现了这个算法,集合必须满足
- 排好序
- 实现List接口
如果集合没有采用Comparable接口的compareTo方法进行排序,还要提供一个比较器对象。
i = Collections.binarySearch(c, element);
i = Collections.binarySearch(c, element, comparator);
如果binarySearch方法返回的数值大于等于0,则表示匹配对象的索引,c.get(i)即为查找的element。如果返回负值,则表示没有匹配的元素。但是,可以利用返回值计算应该将element插入到集合的哪个位置。
if(i<0)
c.add(-i - 1, element);
只有采用随机访问,二分查找才有意义。如果为binarySearch算法提供一个链表,它将自动地变成线性查找。binarySearch方法检查列表参数是否实现了RandomAccess接口来实现这个特性。
3)简单算法
Collections类中的常用算法:
- min/max
- copy
- fill
- swap
- reverse
- rotate(将索引i的条目移动到(i+d)%l.size())
- frequency(集合中与对象o相同的元素个数)
- disjoint(若两集合没有共同的元素,则返回true)
4)编写自己的算法
在返回类型和参数类型上,根据方法要实现的功能尽量选择更通用的类型/接口。
例如,方法中只需要访问所有元素,这是Collection接口的基本功能,就提供Collection的参数。
示例,返回AbstractList的匿名子类,实现不复制所有菜单项而只提供不可修改的菜单项视图的功能:
List<MenuItem> getAllItems(final JMenu menu)
{
return new AbstractList<MenuItem>()
{
public MenuItem get(int i)
{
return item.getItem(i);
}
public int size()
{
return item.getItemCount();
}
};
}
5)遗留的集合
1.Hashtable类
Hashtable类与HashMap类的作用一样,它们拥有相同的接口。Hashtable的方法也是同步的。
2.枚举
遗留集合使用Enumeration接口对元素序列进行遍历。Enumeration接口有两个方法,即hasMoreElements和nextElement。这两个方法与Iterator接口的hasNext方法和next方法十分类似。
Enumeration<Employee> e = staff.elements();
while( e.hasMoreElements())
{
Employee e = e.nextElement();
}
静态方法Collections.enumeration将产生一个枚举对象,枚举集合中的元素。
List<InputStream> streams = ...;
SequenceInputStream in = new SequenceInputStream(Collections.enumeration(streams));
//the SequenceInputStream constructor expects an enumeration
3)属性映射表
属性映射表(property map)是一个特殊的映射表结构,有以下特性:
- 键与值都是字符串
- 表可以保存到一个文件中,也可以从文件中加载
- 使用一个默认的辅助表
Properties类的重要方法
- getProperty(String key)
- getProperty(String key, String defaultValue) //若映射表中不存在,则返回默认字符串
- load(InputStream in)
- store(OutputStream out, String commentString)
4)栈
Stack类定义了push,pop,peek的方法。pop和peek不应在栈为空时调用。
5)位集
BitSet类用于存放一个位序列,使用位集要比使用Boolean对象的ArrayList更加高效。
bitSet.get(i)返回boolean值,true为开,false为关
bitSet.set(i)将一位置1
bitSet.clear(i)将一位清0
void and(BitSet set)
void or(BitSet set)
void xor(BitSet set)
void andNot(BitSet set) (清楚位集中对应另一个位集的所有位)