Java编程思想 Ch17 容器深入研究
17.1 完整的容器分类法
17.2 填充容器
package Ch16;
import java.util.*;
class StringAddress{
private String s;
public StringAddress(String s){
this.s = s;}
public String toString(){
return super.toString()+" "+s;
}
}
public class FillingLists {
public static void main(String[] args) {
List<StringAddress>list = new ArrayList<>(Collections.nCopies(4,new StringAddress("Hello")));
System.out.println(list);
Collections.fill(list,new StringAddress("World"));
System.out.println(list);
}
}
/*[aaa@qq.com Hello, aaa@qq.com Hello, aaa@qq.com Hello, aaa@qq.com Hello]
[aaa@qq.com World, aaa@qq.com World, aaa@qq.com World, aaa@qq.com World]*/
17.2.1 一种Generator的解决方法
package Ch17;
import Ch15.Generator;
import java.util.ArrayList;
public class CollectionData<T> extends ArrayList<T> {
public CollectionData(Generator<T> gen, int quantity){
for(int i=0; i<quantity; i++){
add(gen.next());
}
}
public static <T> CollectionData<T> list(Generator<T> gen, int quantity){
return new CollectionData<T>(gen, quantity);
}
}
import Ch15.Generator;
public class Goverment implements Generator<String> {
String[] foundation = ("strange women lying in ponds "+"disturbing swords is no basis for a system of "+"goverment" ).split(" ");
private int index;
public String next(){
return foundation[index++];
}
}
import java.util.*;
public class CollectionDataTest {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<>(new CollectionData<String>(new Goverment(), 15));
set.addAll(CollectionData.list(new Goverment(), 15));
System.out.println(set);
}
}
output:
[strange, women, lying, in, ponds, disturbing, swords, is, no, basis, for, a, system, of, goverment]
17.2.2 Map生成器
可以使用工具来创建如何用于Map或Collection得生成数据集,然后通过构造器或Map.putAll()和Collectioin.addAll()方法来初始化Map和Collection。
17.2.3 使用Abstract类
17.3 Collection的功能方法(不包含Map)
boolean add(T) | 注意返回值是boolean,表示是否成功加入 |
---|---|
boolean addAll(Collection) | |
void clear() | |
boolean contains(T) | // Map中用到 |
Boolean containsAll(Collection) | // |
boolean isEmpty() | |
Iterator iterator() | |
Boolean remove(Object) | |
boolean removeAll(Collection) | |
Boolean retainAll(Collection) | |
int size() | |
Object[] toArray() | 返回一个数组包含容器中所有元素 |
T[] toArray(T[] a) | 返回结果的运行时参数和参数数组a的类型相同,而不只是Object |
17.4 可选操作???
没有理解p472页的原因
执行各种添加和删除的方法在Collection接口中都是可选操作,意味着实现类并不需要为这些方法提供功能定义。
未获支持的操作只有在运行时才能探测到,因此它们表示动态类型检查。
17.4.1 未获支持的操作
最常见的未获支持操作,都来源于背后固定尺寸的数据结构支持的容器。比如Arrays.asList()将数组转换为List时
17.5 List的功能方法
add添加对象,get取出元素,iterator()获取Iterator
17.6 Set 和存储顺序
Set(interface) | 不多次保存重复元素。必须定义equals方法(因为是接口,所以所有Set实现都要定义equals)。不保证维护元素次序。 |
---|---|
HashSet* | 为快速查找设计。必须定义hashCode()方法。 |
TreeSet | 保持次序的Set,底层为树。元素必须实现Comparable接口。 |
LinkedHashSet | 具有HashSet查询速度,内部使用链表维护元素顺序(插入的顺序)。必须定义hashCode方法。 |
package Ch17;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
class SetType{
int i;
public SetType(int n){i=n;}
public boolean equals(Object o){
return o instanceof SetType && (i==((SetType)o).i);
}
public String toString(){
return Integer.toString(i);
}
}
class HashType extends SetType{
public HashType(int n){super(n);}
public int hashCode(){return i;}
}
// 实现了Comparable接口,如果一个对象被用于任何种类的排序容器中,必须实现这个接口。
class TreeType extends SetType implements Comparable<TreeType>{
public TreeType(int n){super(n);}
public int compareTo(TreeType arg){
return (arg.i<i?-1:(arg.i==i?0:1));
}
}
/*
关于compareTo和equals方法。 equals返回true,compareTo返回0,equals返回false,compareTo返回非零值。
*/
public class TypesForSets {
static<T> Set<T> fill(Set<T> set, Class<T> type){
try{
for(int i=0; i<10; i++)
set.add(type.getConstructor(int.class).newInstance(i));
}catch(Exception e){
throw new RuntimeException(e);
}
return set;
}
static<T> void test(Set<T> set, Class<T> type){
// 尝试添加重复元素
fill(set, type);
fill(set, type);
fill(set, type);
System.out.println(set);
}
public static void main(String[] args) {
test(new HashSet<HashType>(), HashType.class);
// HashSet 以某种顺序保存元素,[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
test(new LinkedHashSet<HashType>(),HashType.class );
//LinkedHashSet按照元素插入顺序维护元素,[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
test(new TreeSet<TreeType>(),TreeType.class );
// TreeSet按照排序顺序插入元素,[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
// things that dont work
// SetType 和 TreeTpye只有默认的hash方法
test(new HashSet<SetType>(), SetType.class);
//[7, 7, 0, 8, 5, 9, 4, 1, 1, 3, 2, 3, 1, 9, 2, 8, 4, 6, 5, 4, 6, 8, 0, 3, 7, 5, 0, 6, 2, 9]
test(new HashSet<TreeType>(), TreeType.class);
// [7, 7, 0, 8, 5, 9, 4, 1, 1, 3, 2, 3, 1, 9, 2, 8, 4, 6, 5, 4, 6, 8, 0, 3, 7, 5, 0, 6, 2, 9]
test(new LinkedHashSet<SetType>(), SetType.class);
//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
test(new LinkedHashSet<TreeType>(), TreeType.class);
//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
/*
当尝试在TreeSet中使用没有实现Comparable的类型,将会得到异常
*/
try{
test(new TreeSet<SetType>(), SetType.class);
}catch (Exception e){
System.out.println(e.getMessage());
}
//java.lang.ClassCastException: Ch17.SetType cannot be cast to java.lang.Comparable
try{
test(new TreeSet<HashType>(), HashType.class);
}catch (Exception e){
System.out.println(e.getMessage());
}
//java.lang.ClassCastException: Ch17.HashType cannot be cast to java.lang.Comparabl
}
}
17.6.1 SortedSet
保证处于排序状态。
Comparator comparator() | 返回当前Set使用的Comparator 或者null表示自然排序 |
---|---|
Object first() | 返回容器中第一个元素 |
Object last() | 返回容器中最后一个元素 |
SortedSet subSet(fromElement, toElement) | 生成Set的子集,从fromElement到toElement |
SortedSet headSet(toELement) | 生成Set的子集,由小于toElement元素组成。 |
SortedSet tailSet(fromElement) | 由大于和等于fromElement的元素组成 |
17.7 队列
Queue的仅有两个实现是LinkedList和PriorityQueue。差别在于排序,而不是性能。
package Ch17;
import Ch15.Generator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.PriorityBlockingQueue;
public class QueueBehavior {
private static int count = 10;
static <T> void test(Queue<T> queue, Generator<T> gen){
for(int i=0; i<count; i++)
queue.offer(gen.next());
while(queue.peek()!=null)
System.out.print(queue.remove()+" ");
System.out.println();
}
static class Gen implements Generator<String>{
String[] s = ("one two three four five six seven eight nine ten").split(" ");
int i;
public String next(){return s[i++];}
}
public static void main(String[] args) {
test(new LinkedList<String>(),new Gen());
test(new PriorityQueue<>(),new Gen());
test(new ArrayBlockingQueue<String>(count),new Gen());
test(new ConcurrentLinkedQueue<String>(),new Gen());
test(new LinkedBlockingDeque<>(),new Gen());
test(new PriorityBlockingQueue<>(),new Gen());
}
}
// 除了优先级队列,Queue将按照插入顺序保存元素。
/*one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
*/
17.7.1 优先级队列
package Ch17;
import java.util.*;
public class ToDoList extends PriorityQueue<ToDoList.ToDoItem>{
static class ToDoItem implements Comparable<ToDoItem>{
private char primary;
private int secondary;
private String item;
public ToDoItem(String td, char pri, int sec){
primary = pri;
secondary = sec;
item = td;
}
public int compareTo(ToDoItem arg){
if(primary>arg.primary)
return +1;
if (primary==arg.primary){
if(secondary>arg.secondary)
return +1;
else if(secondary==arg.secondary)
return 0;
}
return -1;
}
public String toString(){
return Character.toString(primary)+secondary+": "+item;
}
}
public void add(String td, char pri, int sec){
//add(new ToDoList.ToDoItem(td, pri, sec));
super.add(new ToDoItem(td, pri, sec));
}
public static void main(String[] args) {
ToDoList toDoList = new ToDoList();
toDoList.add("Empty trash", 'C',4);
toDoList.add("Free dog", 'A',2);
toDoList.add("Free bird",'B',7);
toDoList.add("Mow lawn",'C',3);
toDoList.add("Water lawn",'A',1);
toDoList.add("Free cat",'B',1);
while(!toDoList.isEmpty()){
System.out.println(toDoList.remove());
}
}
}
/*A1: Water lawn
A2: Free dog
B1: Free cat
B7: Free bird
C3: Mow lawn
C4: Empty trash
*/
17.7.2 双向队列 ??
java标准库中没有任何显示用于双向队列的接口。通过组合来创建一个Deque类。
package Ch17;
import java.util.LinkedList;
public class Deque<T> {
private LinkedList<T> deque = new LinkedList<T>();
public void addFirst(T e){deque.addFirst(e);}
public void addLast(T e){deque.addLast(e);}
public T getFirst(){return deque.getFirst();}
public T getLast(){return deque.getLast();}
public T removeFirst(){return deque.removeFirst();}
public T removeLast(){return deque.removeLast();}
public int size(){return deque.size();}
public String toString(){return deque.toString();}
}
17.8 理解Map
映射表(关联数组)
17.8.1 性能
HashMap | 通过构造器设置容量和负载因子,调整容器性能。 |
---|---|
LinkedHashMap | 类似HashMap,遍历时是插入顺序,或者最近最少使用。比HashMap慢一点,迭代访问更快。 |
TreeMap | 基于红黑树的实现,由Comparator决定次序,唯一带有subMap的Map,返回子树。 |
WeakHashMap | 弱键映射,允许释放映射使用的键。 |
CouncurrentHashMap | 一种线程安全的map,不涉及同步加锁。 |
IdentityHashMap | 使用==,代替equals对键进行比较。 |
任何键都要有equals方法和hashCode。用于TreeMap还要实现Comparable。
17.8.2 SortedMap
使用SortedMap(TreeMap是现阶段唯一实现),
comparator,firstKey,lastKey,subMap(fromKey, toKey), headMap(toKey)键值小于toKey的所有键值对,tailMap(fromKey)键大于等于fromKey的所有键值。
17.8.3 LinkedHashMap
package Ch17;
import java.util.LinkedHashMap;
import static tools.Print.print;
import net.mindview.util.*;
public class LinkedHashMapDemo {
public static void main(String[] args) {
LinkedHashMap<Integer,String> linkedMap = new LinkedHashMap<>(new CountingMapData(9));
print(linkedMap);
//使用最近最少使用LRU
linkedMap = new LinkedHashMap<>(16,0.75f,true);
linkedMap.putAll(new CountingMapData(9));
print(linkedMap);
for(int i= 0;i<6; i++){
linkedMap.get(i);
}
print(linkedMap);
linkedMap.get(0);
print(linkedMap);
}
}
/*{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{6=G0, 7=H0, 8=I0, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0}
{6=G0, 7=H0, 8=I0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0}*/
17.9 散列与散列码
标准类库中的类都可用作HashMap的键,覆盖了HashCode方法。但自己创建的类,提供必需的方法。
Object的hashCode方法使用对象的地址计算散列码,不同对象即使内容相同,散列码也是不同的。
默认的equals方法也是比较对象地址。
所以,要覆盖hashCode方法,同时更需要覆盖equals方法(hashCode是否一致,根据对象是否一致)。
正确的equals方法满足:
自反性 | 对任意x,x.equals(x)一定返回true |
---|---|
对称性 | 对任意x和y,x.equals(y)和y.equals(x)结果一致 |
传递性 | 对任意x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)页返回true |
一致性 | 对任意x和y,如果对象中用与比较的信息没有改变,无论调用多少次equals,结果都一样。 |
null | 对任何不是null的x,x.equals(null)一定返回false |
17.9.1 理解hashCode()
package Ch17;
import java.util.*;
import net.mindview.util.*;
public class SlowMap<K,V> extends AbstractMap<K,V> {
private List<K> keys = new ArrayList<>();
private List<V> values = new ArrayList<>();
//返回旧键值或者null
public V put(K key, V value){
V oldValue = get(key);// The old value or null
if(!keys.contains(key)){
keys.add(key);
values.add(value);
}else{
values.set(keys.indexOf(key),value);
}
return oldValue;
}
// get参数是Object的。泛型来得太晚导致的。
public V get(Object key){
if(!keys.contains(key))
return null;
return values.get(keys.indexOf(key));
}
//返回视图
// Map.Entry是一个接口,想要创建自己的Map类型,必须同时定义Map.Entry的实现。
public Set<Map.Entry<K,V>> entrySet(){
Set<Map.Entry<K,V>> set = new HashSet<Map.Entry<K,V>>();
Iterator<K> ki = keys.iterator();
Iterator<V> vi = values.iterator();
while(ki.hasNext())
set.add(new MapEntry<K,V>( ki.next(), vi.next()) );
return set;
}
public static void main(String[] args) {
SlowMap<String ,String> m = new SlowMap<String,String>();
m.putAll(Countries.capitals(5));
System.out.println(m);
System.out.println(m.get("BULGARIA"));
System.out.println(m.entrySet());
}
}
/*output:
ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone}
null
[ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone]
*/
package Ch17;
import java.util.Map;
// Map.Entry的实现,但并不恰当。创建了键值的副本,而不是视图。副本不能提供对原始映射表的修改。视图可以。
public class MapEntry<K,V> implements Map.Entry<K,V> {
private K key;
private V value;
public MapEntry(K key, V value){
this.key = key;
this.value = value;
}
public K getKey(){ return key;}
public V getValue(){return value;}
public V setValue(V v){
V result = value;
value = v;
return result;
}
/*因为MapEntry实例是作为entrySet中集的元素,必须定义equals方法,而set实现是HashSet必需定义hashCode方法*/
//重新定义该类的hashCode
public int hashCode(){
return (key==null?0:key.hashCode())^(value==null?0:value.hashCode());
}
//重新定义该类的equals
public boolean equals(Object o){
if(!(o instanceof MapEntry)) return false;
MapEntry me = (MapEntry) o;
return
(key==null?me.getKey()==null:key.equals(me.getKey()))&&(value==null?me.getValue()==null:value.equals(me.getValue()));
}
public String toString(){
return key+"="+value;
}
}
17.9.2 为速度而散列
SlowMap的问题在于键的查找速度,线性查找是最慢的方式。
散列的价值在于速度:散列使得查询快速进行。解决方法之一:保持键的排序状态,使用Collections.binarySearch进行查询。散列则跟进一步。
package Ch17;
import java.util.*;
import net.mindview.util.*;
public class SimpleHashMap<K,V> extends AbstractMap<K,V>{
// 为使得散列均匀分布,桶数使用质数。(事实2的整数次方)
static final int SIZE = 997;
// you can't hava a physical array of generics, but you can upcast to one;
// 本质不是泛型数组,引用是。
@SuppressWarnings("unchecked")
LinkedList<MapEntry<K,V>>[] buckets =(LinkedList<MapEntry<K,V>>[]) new LinkedList[SIZE];
// put方法
public V put(K key, V value){
V oldValue = null;
//确定slot(bucket)
int index = Math.abs(key.hashCode())%SIZE;
if(buckets[index]==null) buckets[index] = new LinkedList<MapEntry<K, V>>();
LinkedList<MapEntry<K,V>> bucket = buckets[index];
// 存储的是MapEntry类对象。
MapEntry<K,V> pair = new MapEntry<>(key,value);
boolean found = false;
// put因为可能会更改值,所以用迭代器,而后面get可以用foreach语法。
// 使用 listIterator 而不简单是iterator(只有单向遍历功能,不能改变集合)
ListIterator<MapEntry<K,V>> it = bucket.listIterator();
while(it.hasNext()){
MapEntry<K,V> iPair = it.next();
if(iPair.getKey().equals(key)){
oldValue = iPair.getValue();
it.set(pair);
found = true;
break;
}
}
if(!found)
buckets[index].add(pair);
return oldValue;
}
public V get(Object key){
int index = Math.abs(key.hashCode())%SIZE;
if(buckets[index]==null) return null;
for(MapEntry<K,V> iPair : buckets[index]){
if(iPair.getKey().equals(key))
return iPair.getValue();
}
return null;
}
public Set<Map.Entry<K,V>> entrySet(){
Set<Map.Entry<K,V>> set = new HashSet<>();
for(LinkedList<MapEntry<K,V>> bucket : buckets){
if(bucket == null) continue;
for( MapEntry<K,V> mpair : bucket)
set.add(mpair);
}
return set;
}
public static void main(String[] args) {
SimpleHashMap<String,String> m = new SimpleHashMap<>();
m.putAll(Countries.capitals(5));
System.out.println(m);
System.out.println(m.get("ERITREA"));
System.out.println(m.entrySet());
}
}
/*
{ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone}
null
[ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone]
*/
17.9.3 覆盖hashCode()
hashCode的生成结果经过处理后生成桶位的下标。
hashCode必须速度快且有意义。即必须根据对象的内容(易变不可)生成散列码。散列码不是独一无二的,更应该关注速度而不是唯一性。但通过hashCode和equals可以唯一确定。
hashCode指导
给int变量result赋予某个非零常量值,如17;
域类型 计算 boolean c = (f?0:1) byte,char, short,int c=(int) f long c = (int)(f^(f>>>32)) float c=Float.floatToIntBits(f) double long l = Double.doubleToLongBits(f) c=(int)(l^(l>>>32)) Object c = f.hashCode() 数组 按上操作 合并计算散列码 result = 37*result+c;
4.返回result
5.检查hashCode,确保相同对象有相同散列码
package Ch17;
import java.util.*;
import static tools.Print.print;
/*
CountedString由String和id两个域来组成。
hashCode和equals都基于CountedString这两个域来生成结果。只基于其中某一个,不同对象可能产生相同值。
*/
public class CountedString {
private static List<String> created = new ArrayList<>();
private String s;
private int id = 0;
public CountedString(String str){
s = str;
created.add(s);
for(String s2 : created){
if(s2.equals(s))
id++;
}
}
public String toString(){
return "String: "+s+" id:"+id+" hashCode(): "+hashCode();
}
public int hashCode(){
int result = 17;
//好像先后顺序对散列码也有影响。大的码先加可以使得散列码小和更均匀??
result = 37*result+s.hashCode();
result = 37*result+id;
return result;
}
// 重新定义equals
public boolean equals(Object o){
return o instanceof CountedString && s.equals(((CountedString)o).s)&& id== (((CountedString)o).id);
}
public static void main(String[] args) {
Map<CountedString,Integer> map = new HashMap<>();
CountedString[] cs = new CountedString[3];
for(int i=0; i<cs.length; i++){
cs[i]=new CountedString("hi");
map.put(cs[i],i);
}
print(map);
for(CountedString cstring : cs){
print("Looking up "+cstring);
print(map.get(cstring));
}
}
}
/*{String: hi id:2 hashCode(): 146448=1, String: hi id:3 hashCode(): 146449=2, String: hi id:1 hashCode(): 146447=0}
Looking up String: hi id:1 hashCode(): 146447
0
Looking up String: hi id:2 hashCode(): 146448
1
Looking up String: hi id:3 hashCode(): 146449
2
*/
package Ch17;
public class Individual implements Comparable<Individual>{
private static long counter = 0;
private final long id = counter++;
private String name;
public Individual(String name){this.name = name;}
public Individual(){}
public String toString(){
return getClass().getSimpleName()+(name==null?"":" "+name);
}
public long id(){return id;}
//这个类的equals 只考虑了成员id。
public boolean equals(Object o){
return o instanceof Individual && id == ((Individual)o).id;
}
public int hashCode(){
int result = 17;
if(name==null)
result = 37*result+name.hashCode();
result = 37*result+(int)id;
return result;
}
public int compareTo(Individual arg){
//不能用equals
String first = getClass().getSimpleName();
String argFirst = arg.getClass().getSimpleName();
int firstCompare = first.compareTo(argFirst);
if(firstCompare!=0)
return firstCompare;
if(name!=null && arg.name!=null){
//调用了标准库类自身的compareTO
int secoundCompare = name.compareTo(arg.name);
if(secoundCompare!=0)
return secoundCompare;
}
return (arg.id<id?-1:(arg.id==id?0:1));
}
}
17.10 选择接口的不同实现
Hashtable、Vector、Stack是遗留类,为了支持老程序。不要使用。
对List的选择
随机访问ArrayList,大量LinkedList除和插入。
固定数量的元素,既可选择背后有数组支撑的List(List<> list = new ArrayLlst<>()) ,也可选择数组。
对Set的选择
HashSet添加和查找性能最好。
TreeSet存在唯一原因是排序。迭代比HashSet快。
LinkedHashSet维护了插入顺序链表,迭代速度快,所以插入代价比HashSet高。
对Map的选择
除了IdentityHashMap(使用==比较元素),所有的Map实现的插入操作都会随着Map尺寸变大而明显变慢。查找代价比插入小很多。
HashMap的性能因子
容量:表中桶位数。 初始容量:创建时桶位数。 尺寸:当前储存的项数。
负载因子:尺寸/容量。 越轻表示冲突的可能性越小。
HashSet和HashMap(默认0.75)都有指定负载因子的构造器。
更高的负载因子,可以降低空间需求,但会增加查找代价。
当负载达到设置值时,将自动增加容量–大致使容量加倍,并将现有对象再散列。
17.11 实用方法
java.util.Collections类内部的静态方法
checkedCollection(Collection, Class type) | |
---|---|
max(Collection) min(Collection) | 采用Collection内置自然比较法 |
max(Collection, Comparator) min(Collection,Comparator) | 采用提供的Comparator比较 |
indexOfSubList(List source, List target) | 返回target在source中最后一次出现的位置或者没找到时返回-1 |
replaceAll(List, T oldVal, T newVal) | |
reverse(List) | 逆转所有元素次序 |
reverseOrder() | 返回一个实现了Comparator集合的逆转Comparator |
reverseOrder(Comparator) | 逆转该Comparator |
rotate(List, int distance) | 所有元素向后移动distance位置,将末尾元素移动到开头 |
shuffle(List) shuffle(List, Random) | 随机改变指定列表的顺序 |
sort(List) sort(List, Comparator c) | 排序 |
copy(List dest, List src ) | 将src中的元素复制到dest |
swap(List, int i, int j) | 交换list中位置i与位置j的元素,通常比自己代码快?? |
fill(List, T x) | 用对象x填充list |
nCopies(int n, T x) | 返回大小为n的List,此list不可改变,其中引用指向x |
disjoint(Collection, Collection) | 当两个集合没有相同元素时,返回true |
frequency(Collection, Object x) | x出现次数 |
emptyList() emptyMap() emptySet() | 返回不可变的空list,set 和map |
singleton(T x) singletonList(T x) singletonMap(K key, V value) | 产生不可变的set list map 只包含单一项 |
binarySearch(List, T key, Comparator) | 在对list排序后,且使用相同Comparator |
unmodifiableCollection(Collection ) unmodifiableList(List) unmodifiableSet(set) unmodifiableMap(map) | 返回集合只读的副本 |
17.11.1 List的排序和查询
17.11. 2 设定Collection或Map为不可修改
即创建只读容器。
17.11.3 Collection或Map的同步控制
Collection<String> c = Collection.synchronizedCollection(new ArrayList<String>())
将新生成的容器传递给了适当的“同步”方法,这样就不会有任何集合暴露出不同步的版本。
快速报错
Java容器有一种保护机制,能够防止多个进程同时修改一个容器的内容。
ConcurrentModificationException异常
Collection<String> c = new ArrayList<>();
Iterator<String> it = c.iterator();
c.add("An");
//修改了集合后,应该重新获取迭代器。
try{
String s = c.next();
}catch(ConcurrentModificationException e){
System.out.println(e);
}
/*
output: java.util.ConcurrentModificationException
*/
ConcurrenHashMap,CopyOnWriteArrayList和CopyOnWriteArraySet都使用了可以避免该异常的技术。
17.12 持有引用
对象是可获得的(reachable)则垃圾回收机制不能释放该引用。
三个继承抽象类Reference的类:softReference,WeakReference,PhantomReference,
作为你和普通引用之间的代理。当你希望继续使用这个对象,而在内存消耗殆尽时,又希望释放对象,用该类。且不能有另外的普通引用指向这个对象。
17.12.1 WeakHashMap
17.13 Java 1.0/1.1 的容器
永远不要使用,能读懂。
17.13.1 Vector 和 Enumeration
package Ch17;
import net.mindview.util.Countries;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Vector;
// Enumeration类似迭代器。
public class Enumerations {
public static void main(String[] args) {
Vector<String> v = new Vector<>(Countries.names(10));
Enumeration<String> e = v.elements();
//类似hasNext
while(e.hasMoreElements()){
// 类似next
System.out.print(e.nextElement()+" ");
}
// 将Iterator转为Enumeration
e = Collections.enumeration(new ArrayList<String>());
}
}
17.13.2 Hashtable(由HashMap替代)
17.13.3 Stack
继承自Vector。push,pop。
需要栈使用LinkedList或者11章自己写的栈