集合:
队列(queue)接口:可以在队尾添加元素,在队头删除元素,并可以查找队列中元素的个数。收集对象时,按照“先进先出”的规则检索对象。
interface Queue<E>{ ... }
队列的两种实现方式:一种是使用循环数组;另一种是使用链表。
集合类的基本接口是Collection接口,这个接口有两个基本方法:
public interface Collection<E> {
boolean add(E element); //向集合中添加元素
Iterator<E> iterator(); //返回一个实现了Iterator接口的对象,可以使用这个迭 ... //代器对象依次访问集合中的元素。
}
一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。
迭代器Iterator接口包含3个方法:
public interface Iterator<E>{
E next();
boolean hasNext();
void remove();
}
通过反复调用next()方法,可以逐个访问集合中的每个元素,但如果到了集合的末尾,next()方法将抛出NoSuchElementException,因此在调用next之前调用hasNext方法。
如果想要查看集合中的所有元素,就请求一个迭代器,并在hasNext返回true时反复地调用next方法。
迭代器查找一个元素唯一的方法是调用next方法。
for each循环可以与任何实现了Iterable接口的对象一起工作,这个接口只包含一个方法:
public interface Iterable<E>{
Iterator<E> iterator();
}
Collection接口扩展了Iterable接口,因此,对于标准类库中的任何集合都可以使用for each循环。
元素被访问的顺序取决于集合类型。如果对ArrayList进行迭代,迭代器将从索引0开始,每迭代一次,索引值加1。然而,如果访问HashSet中的元素,每个元素将会按照某种随机的次序出现。虽然可以确定在迭代中能够遍历到集合中的所有元素,但却无法预知元素被访问的次序。
删除元素:Iterator接口的remove方法将会删除上次调用next方法时返回的元素。如果想要删除指定位置上的元素,仍然需要越过这个元素。删除字符串集合中第一个元素的方法:
Iterator<String> it = c.iterator();
it.next();
it.remove();
对next方法和remove方法的调用具有互相依赖性,如果调用remove之前没有调用next将是不合法的,会抛出IllegalStateException异常。如果想要删除两个相邻的元素,不能直接这样调用:
it.remove();
it.remove();
必须:
it.remove();
it.next();
it.remove();
检测任意集合是否包含指定元素的泛型方法:contains.
还有其他方法:size、isEmpty、containsAll、equals、addAll、remove、removeAll、clear、retainAll、toArray.
Java类库提供了类AbstractCollection,将size和iterator抽象化了,但其他方法均提供了实现。
具体的集合:
处理以Map结尾的类之外,其它类都实现了Collection接口,而以Map结尾的类实现了Map接口。
链表:Java中的所有链表都是双向链接的。链表是有序集合,并且集合中每个元素的位置十分重要。 和Set不同,List允许有相同的元素。
接口List、类LinkedList
只有在自然有序的集合中使用迭代器添加元素才有意义,因此Iterator接口中就没有add方法,而在子接口ListIterator中包含add方法。ListIterator接口有两个方法可以用来反向遍历链表:
E prevoius()
boolean hasPrevious()
LinkedList类的listIterator方法返回一个实现了ListIterator接口的迭代器对象。
add方法在迭代器位置之前添加一个新对象。如越过链表中的第一个元素,并在第二个元素之前添加新对象。
如果调用previous就会将右侧的元素删除掉,并且不能在同一行中调用两次remove。
add方法只依赖于迭代器的位置,而remove方法依赖于迭代器的状态。
set方法用一个新元素取代调用next或previous方法返回的上一个元素:
用一个新值取代链表的第一个元素:
ListIterator<String> iter = list.listIterator();
String oldValue = iter.next(); //returns first element
iter.set(new Value); //sets first element to newValue
如果需要对集合进行随机访问,就使用数组或ArrayList,而不要使用链表。
有两种访问元素的协议:一种是用迭代器,另一种是用get和set方法随机的访问每个元素。后者不适合链表,但对数组却很有用。
数组列表:
ArrayList:实现了List接口。ArrayList封装了一个动态再分配的对象数组。
Vector类:其所有方法都是同步的,可以由两个线程安全地访问一个Vector对象。
ArrayList方法不是同步的。
为了 避免执行成本较高的随机访问操作,引入了一个标记接口RandomAccess。这个接口没有任何方法,但可以用来检测一个特定的集合是否支持高效的的随机访问。
散列集(hash table):
如果不在意元素的顺序,可以有几种能够快速查找元素的数据结构,其缺点是无法控制 元素出现的次序,它们将按照有利于其操作目的的原则组织数据。
散列表为每个对象计算一个整数,称为散列码(hash code)。
a.equals(b)为true,则a与b必须具有相同的hash code。
set是没有重复元素的元素集合,set的add方法首先在集中查找要添加的对象,如果不存在,就将这个对象添加进去。
HashSet类是实现了基于散列表的集,其contains方法以及被重新定义,用来快速查看是否已经出现在集中,它只在某个桶中查找元素,而不必查看集合中的所有元素。
散列表迭代器将依次访问所有的桶,由于散列将元素分散在表的各个位置上,所以访问他们的顺序几乎是随机的。只有不关心集合中元素的顺序时才应该使用HashSet。
树集(TreeSet):是一个有序集合,可以以任意顺序将元素插入到集合中。在对集合进行遍历时,每个值将自动地按照排序后的顺序呈现。
排序使用树结构完成的,每次将一个元素添加到树中时,都被放置在正确的排序位置上。因此,迭代器总是以排好序的顺序访问每个元素。
将一个元素添加到TreeSet中要比添加到HashSet中慢。但与将元素添加到数组或链表的正确位置上相比还是快很多。
在默认情况下,树集假定插入的元素实现了Comparable接口,其中定义了一个方法:compareTo
双端队列:可在首尾同时添加或删除元素。不支持在队列中间添加元素。
Deque接口,并由ArrayDeque和LinkedList类实现
优先级队列(PriorityQueue)中的元素可以按照任意的顺序插入,却总是按照排序的顺序进行检索。即,无论何时调用remove方法,总会获得当前优先级队列中最小的元素。然而,优先级队列并没有对所有的元素进行排序。若果用迭代的方式处理这些元素,并不需要对他们进行排序。优先级队列使用了堆。堆是一个可以自我调整的二叉树,对树执行添加和删除操作,可以让最小的元素移动到根,而不必花时间对元素进行排序。
使用优先级队列的典型示例是任务调度。
映射表(Map):用来存放键/值对,如果提供了键,就能查找到值。
提供两个实现:HashMap和TreeMap,都实现了Map接口。
HashMap对键进行散列,TreeMap用键的整体顺序对元素进行排列,并将其组织成搜索树。散列或比较函数只能作用于键,与键关联的值不能进行散列或比较。
与集一样,散列稍微快一点,如果不需要按照排列顺序访问键,就最好选择散列。
如果对同一个键两次调用put方法,第二个值就会取代第一个值。实际上,put将返回用这个键参数存储的上一个值。
如果想要同时查看键与值,就可以通过枚举各个条目(entries)查看,就避免对值进行查找。可以使用下面这段代码框架:
for(Map.Entry<String,Employee> entry:staff.entrySet()){
String key = entry.getKey();
Employee value = entry.getValue();
...
}
专用集与散列表集:
1、弱散列映射表(WeakHashMap)
2、链接散列集(LinekedHashSet)和链接映射表(LinkedHashMap):用来记住插入元素项的顺序。避免在散列表中的项从表面上看是随机排列的。
3、枚举集(EnumSet)与映射表(EnumMap)
4、标识散列映射表(IdentityHashMap)
视图与包装器
批操作:避免频繁使用迭代器
假设找出两个集合的交集:
Set<String> result = new HashSet<>(a);
result.retainAll(b);
假设希望将一个列表的前10个元素添加到另一个容器中,可以建立一个子列表用于选择前10个元素:
relocated.addAll(staff.subList(0,10));
这个子范围也可以成为更改操作的对象。
staff.subList(0,10).clear();
集合与数组之间的转换:
将数组转换为集合,Arrays.asList的包装器就可以实现:
String[] values = ...;
HashSet<String> staff = new HashSet<>(Arrays.asList(values));
集合转换为数组:设计为所希望的元素类型且长度为0的数组
String[] values = staff.toArray(new String[0]);
可以构造一个指定大小的数组:
staff.toArray(new String[staff.size()]); //在这种情况下, 没有创建任何新数组。
Collections.sort(staff);
实现对List接口的集合进行排序。
若谷想采用其他方式对列表进行排序,可以将Comparator对象作为第二个参数传递给sort方法。
如果想按照降序对列表进行排序,可以使用:
Collections.sort(staff,Collections.reverseOrder());
Collections类有一个算法Shuffle,其功能与排序刚好相反,即随机的混排列表中元素的顺序。
如果提供的列表没有实现RandomAccess接口,shuffle方法将元素复制到数组中,然后打乱数组元素的顺序,最后再将打乱顺序后的元素复制回列表。
二分查找:Collections类的binarySearch方法实现了这个算法,注意:集合必须是排好序的,要想查找某个元素,必须提供集合(这个集合要实现List接口)以及要查找的元素。如果集合没有采用Comparable接口的compareTo方法进行排序,就还要提供一个比较器对象。
遗留的集合:
Hashtable类:与HashMap类的作用一样,实际上它们拥有相同的接口。
与Vector类的方法一样。Hashtable的方法也是同步的。如果对同步性或与遗留代码的兼容性没有任何要求,就应该使用HashMap。
枚举
属性映射表
栈(Stack类)
位集(BitSet类):用于存放一个位序列