1. 程式人生 > >Java-Java程式設計思想第四版 第十七章 容器深入研究 練習

Java-Java程式設計思想第四版 第十七章 容器深入研究 練習

練習1:

/* Create a List (try both ArrayList and LinkedList) and fill it using
* Countries. Sort the list and print it, then apply Collections.shuffle()
* to the list repeatedly, printing it each time so that you can see how 
* the shuffle() method randomizes the list differently each time.
*/
import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class Ja17_1{
    public static void main(String[] args){
        List<String> ls=new ArrayList<String>(Countries.names(6));
        Collections.sort(ls);
        print(ls);
        Collections.shuffle(ls);
        print(ls);
        Collections.shuffle(ls);
        print(ls);
    }
}



練習2:
// Produce a Map and a Set containing all the countries that begin
// with 'A'.

import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;
import java.util.regex.*;
public class Ja17_2{
    public static void main(String[] args){
        Map<String,String> mp=new HashMap<String,String>();
        for(int i=0;i<Countries.DATA.length;i++)
        if(Pattern.compile("A\\w+").matcher(Countries.DATA[i][0]).matches())
            mp.put(Countries.DATA[i][0],Countries.DATA[i][1]);
        Set<String> se=mp.keySet();
        print(mp);
        print(se);


    }
}



練習3:
/* Using Countries, fill a Set multiple times with the same data 
* and verify that the Set ends up with only one of each instance.
* Try this with HashSet, LinkedHashSet, and TreeSet.
*/
import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;
import java.util.regex.*;
public class Ja17_3{
    public static void main(String[] args){
        Set<String> hs=new HashSet<String>(Countries.names(6));
        Set<String> ts=new TreeSet<String>(Countries.names(6));
        Set<String> ls=new LinkedHashSet<String>(Countries.names(6));
        print(hs);
        print(ts);
        print(ls);
    }
}



練習4:
/* Create a Collection initializer that opens a file and breaks
* it into words using TextFile, and then uses the words as the
* source of data for the resulting Collection. Demonstrate that 
* it works.
*/

import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;

public class Ja17_4{
    public static void main(String[] args){
        String[] s=TextFile.read("Ja17_3.java").split("\\W+");
        Set<String> se=new LinkedHashSet<String>(Arrays.asList(s));
        print(se);

    }
}



練習5:
// Modify Ja17_5.java to fully implement the flyweight
// by adding a custom EntrySet class like the one in Countries.java.
// See also alternate solution CountingMapData5Alt.java. 
import java.util.*;

public class Ja17_5 extends AbstractMap<Integer,String> {
    private static String[] chars =
        "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
        .split(" ");
    private static class Entry implements Map.Entry<Integer,String> {
        int index;
        Entry(int index) { this.index = index; }
        public boolean equals(Object o) {
            return Integer.valueOf(index).equals(o);
        }
        public Integer getKey() { return index; }
        public String getValue() {
            return
                chars[index % chars.length] +
                Integer.toString(index / chars.length);
        }
        public String setValue(String value) {
            throw new UnsupportedOperationException();
        }
        public int hashCode() {
            return Integer.valueOf(index).hashCode();
        }
    }
    public class EntrySet extends AbstractSet<Map.Entry<Integer,String>>{
        private int size;
        public EntrySet(int size){this.size=size;}
        class Iter implements Iterator<Map.Entry<Integer,String>>{
            private Entry entry=new Entry(-1);
            public boolean hasNext(){
                return entry.index<size-1;
            }
            public Map.Entry<Integer,String> next(){entry.index++;return entry;}
            public void remove(){}
        }
        public int size(){return size;}
        public Iterator<Map.Entry<Integer,String>> iterator(){
            return new Iter();
        }
  }
  private Set<Map.Entry<Integer,String>> entries=new EntrySet(chars.length);
  public Set<Map.Entry<Integer,String>> entrySet(){return entries;}
  public static Map<Integer,String> select(final int size){
    return new Ja17_5(){
        public Set<Map.Entry<Integer,String>> entrySet(){
            return new EntrySet(size);
        }
    };
  }
  /*
  public Set<Map.Entry<Integer,String>> entrySet() {
    // LinkedHashSet retains initialization order:
    Set<Map.Entry<Integer,String>> entries =new EntrySet<Map.Entry<Integer,String>>();
    for(int i = 0; i < size; i++)
      entries.add(new Entry(i));
    return entries;
  }*/
  public static void main(String[] args) {
    System.out.println(new Ja17_5());
    System.out.println(select(5));
  }
}



練習6:


練習7:
/* Create both an ArrayList and a LinkedList, and fill each using the
*  Countries.names() generator. Print each list using an ordinary 
* iterator, then insert one list into the other by using a ListIterator, 
* inserting at every other location. Now perform the insertion staring
* at the end of the first list and moving backwards.
*/
import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class Ja17_7{
    public static void main(String[] args){
        List<String> ll=new LinkedList<String>(Countries.names(6));
        List<String> al=new ArrayList<String>(Countries.names(6));
        ListIterator<String> lit=ll.listIterator();
        ListIterator<String> lit2=al.listIterator();
        while(lit.hasNext()){
            lit.next();
            if(lit.hasNext())lit.add(lit2.next());
            else {lit.add(lit2.next());break;}
        }
        print(ll);

    }
}



練習8:
/* Create a generic, singly linked list class called SList, which, to keep
* things simple, does not implement the List interface. Each Link object in
* the list should contain a reference to the next element in the list, but
* not the previous one (LinkedList, in contrast, is a doubly linked list, 
* which means it maintains links in both directions). Create your own 
* SListIterator which, again for simplicity, does not implement ListIterator.
* The only method in SList other than toString() should be iterator(), which
* produces an SListIterator. The only way to insert and remove elements from 
* an SList is through SListIterator. Write code to demonstrate SList. 
*/
import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;
class Link<T>{
     Link<T> next;
     T t;
    public Link(T t,Link<T> next){
        this.next=next;
        this.t=t;
    }
    public Link(T t){this(t,null);}
    public String toString(){
        if(t==null)return "null";
        else return t.toString();
    }
}
class SList<T>{
    private Link<T> headLink=new Link<T>(null);
    public SListIterator<T> sListIterator(){
        return new SListIterator<T>(headLink);
    }
    public String toString(){
        if(headLink.next==null)return "SList: []";
        SListIterator<T> sl=this.sListIterator();
        StringBuilder sb=new StringBuilder();
        while(sl.hasNext()){
            sb.append(sl.next());
            sb.append(sl.hasNext()?",":"");
        }
        return sb.toString();
    }
}
class SListIterator<T>{
    Link<T> current;
    public SListIterator(Link<T> headLink){current=headLink;}
    public boolean hasNext(){
        return (current.next!=null);
    }
    public void insert(T t){
        current.next=new Link<T>(t,current.next);
        current=current.next;//
    }
    public Link<T> next(){
        return current=current.next;
    }
    public void remove(){
        if(current.next!=null)current.next=current.next.next;
    }
}
public class Ja17_8{
    public static void main(String[] args){
        SList<String> a=new SList<String>();
        SListIterator<String> sl=a.sListIterator();
        sl.insert("fsd");
        sl.insert("qwe");
        SListIterator<String> sl2=a.sListIterator();
        print(sl2.hasNext());
        sl2.remove();
        print(a);
    }
}



練習9:
// Use RandomGenerator.String to fill a TreeSet, but use alphabetic 
// ordering. Print the TreeSet to verify the sort order.
import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class Ja17_9{
    public static void main(String[] args){
        TreeSet<String> set=new TreeSet<String>();
        RandomGenerator.String a=new RandomGenerator.String();
        for(int i=0;i<5;i++){

            set.add(a.next());
        }
        print(set);
    }
}



練習10:
// Using a LinkedList as your underlying implementation,
// define your own SortedSet.
import net.mindview.util.*;
import java.util.*;
import static net.mindview.util.Print.*;
class SortedSet<T> extends LinkedList<T>{
    int compare(T t1,T t2){
        return (t1.hashCode()<t2.hashCode()?1:(t1.hashCode()==t2.hashCode()?0:-1));
    }
    public boolean add(T t){
        if(this.isEmpty()){add(0,t);}
        if(!this.contains(t)){
            Iterator<T> it=this.iterator();
            int index=0;
            while(it.hasNext()){
                T t1=it.next();
                if(compare(t1,t)==1){}
                else if(compare(t1,t)==-1){index++;}
                else throw new RuntimeException();
            }
            add(index,t);return true;
        }
        return false;
    }
}
public class Ja17_10{
    public static void main(String[] args){
        RandomGenerator.String a=new RandomGenerator.String();
        SortedSet<String> ss=new SortedSet<String>();
        for(int i =0;i<4;i++)
        ss.add(a.next());
        print(ss);
    }
}



練習11:

/* Create a class that contains an Integer that is initialized 
* to a value between 0 and 100 using java.util.Random. Implement
* Comparable using this Integer field. Fill a PriorityQueue with 
* objects of your class, and extract the values using poll() to 
* show that it produces the expected order.
*/
import static net.mindview.util.Print.*;
import java.util.*;
class A implements Comparable<A>{
    Random rand=new Random();
    Integer i=rand.nextInt(100);
    public int compareTo(A a){
        return i>a.i?1:i==a.i?0:-1;
    }
    public String toString(){return i.toString();}
}
public class Ja17_11{
    public static void main(String[] args){
        PriorityQueue<A> pq=new PriorityQueue<A>();
        for(int i=0;i<20;i++)
        pq.offer(new A());
        print(pq);
        for(int j=0;j<pq.size();j++)
            System.out.print(pq.remove()+", ");
    }
}

PS:直接print的結果和poll, 和remove()都不一樣

練習12:
// Substitute a HashMap, a TreeMap and a LinkedHashMap
// in Ja17_12.java's main().
import static net.mindview.util.Print.*;
import java.util.*;

public class Ja17_12<K,V> {
  private Object[][] pairs;
  private int index;
  public Ja17_12(int length) {
    pairs = new Object[length][2];
  }
  public void put(K key, V value) {
    if(index >= pairs.length)
      throw new ArrayIndexOutOfBoundsException();
    pairs[index++] = new Object[]{ key, value };
  }
  @SuppressWarnings("unchecked")
  public V get(K key) {
    for(int i = 0; i < index; i++)
      if(key.equals(pairs[i][0]))
        return (V)pairs[i][1];
    return null; // Did not find key
  }
  public String toString() {
    StringBuilder result = new StringBuilder();
    for(int i = 0; i < index; i++) {
      result.append(pairs[i][0].toString());
      result.append(" : ");
      result.append(pairs[i][1].toString());
      if(i < index - 1)
        result.append("\n");
    }
    return result.toString();
  }
  public static void main(String[] args) {
    HashMap<String,String> map = new HashMap<String,String>();
    LinkedHashMap<String,String> map2 = new LinkedHashMap<String,String>();
    TreeMap<String,String> map3 = new TreeMap<String,String>();
    map.put("sky", "blue");  map.put("grass", "green");   map.put("ocean", "dancing");    map.put("tree", "tall");    map.put("earth", "brown");    map.put("sun", "warm");
    map2.put("sky", "blue");  map2.put("grass", "green");   map2.put("ocean", "dancing");    map2.put("tree", "tall");    map2.put("earth", "brown");    map2.put("sun", "warm");
    map3.put("sky", "blue");  map3.put("grass", "green");   map3.put("ocean", "dancing");    map3.put("tree", "tall");    map3.put("earth", "brown");    map3.put("sun", "warm");
    print(map);
    print(map2);
    print(map3);
  }
}



練習13:

/* Use AssociativeArray.java to create a word-occurrence counter,
* mapping String to Integer. Using the net.mindview.util.TextFile 
* utility in this book, open a text file and break up the words
* in that file using whitespace and punctuation, and count the 
* occurrence of the words in that file.
*/

import net.mindview.util.*;
import static net.mindview.util.Print.*;
import java.util.*;
public class Ja17_13{
    public static  void main(String[] args){
        ArrayList<String> al=new ArrayList<String>(new TextFile("Ja17_12.java","\\W+"));
        Iterator<String> it=al.iterator();
        LinkedHashMap<String,Integer> lhm=new LinkedHashMap<String,Integer>();
        Integer i;
        while(it.hasNext()){
            String mm=it.next();
            i=lhm.get(mm);
            lhm.put(mm,i==null?1:i++);
        }
        print(lhm);
    }
}


練習14:
// Show that java.util.Properties works in the above program (Maps.java).
import java.util.concurrent.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class Ja17_14 {
  public static void printKeys(Map<Object,Object> map) {
    printnb("Size = " + map.size() + ", ");
    printnb("Keys: ");
    print(map.keySet()); // Produce a Set of the keys
  }
  public static void test(Map<Object,Object> map) {
    print(map.getClass().getSimpleName());
    map.putAll(new CountingMapData(25));
    // Map has 'Set' behavior for keys:
    map.putAll(new CountingMapData(25));
    printKeys(map);
    // Producing a Collection of the values:
    printnb("Values: ");
    print(map.values());
    print(map);
    print("map.containsKey(11): " + map.containsKey(11));
    print("map.get(11): " + map.get(11));
    print("map.containsValue(\"F0\"): "
      + map.containsValue("F0"));
    Object key=map.keySet().iterator().next();
    print("First key in map: " + key);
    map.remove(key);
    printKeys(map);
    map.clear();
    print("map.isEmpty(): " + map.isEmpty());
    map.putAll(new CountingMapData(25));
    // Operations on the Set change the Map:
    map.keySet().removeAll(map.keySet());
    print("map.isEmpty(): " + map.isEmpty());
  }
  public static void main(String[] args) {
      test(new Properties());
  }
} 
/*Properties
Size = 25, Keys: [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
0]
Values: [Y0, X0, W0, V0, U0, T0, S0, R0, Q0, P0, O0, N0, M0, L0, K0, J0, I0, H0, G0, F0, E0, D0, C0, B0,
A0]
{24=Y0, 23=X0, 22=W0, 21=V0, 20=U0, 19=T0, 18=S0, 17=R0, 16=Q0, 15=P0, 14=O0, 13=N0, 12=M0, 11=L0, 10=K0,
 9=J0, 8=I0, 7=H0, 6=G0, 5=F0, 4=E0, 3=D0, 2=C0, 1=B0, 0=A0}
map.containsKey(11): true
map.get(11): L0
map.containsValue("F0"): true
First key in map: 24
Size = 24, Keys: [23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
map.isEmpty(): true
map.isEmpty(): true*/



練習15:
// Repeat Exercise 13 using a SlowMap. 
/* Use AssociativeArray.java to create a word-occurrence counter,
* mapping String to Integer. Using the net.mindview.util.TextFile 
* utility in this book, open a text file and break up the words
* in that file using whitespace and punctuation, and count the 
* occurrence of the words in that file.
*/
import containers.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
import java.util.*;
public class Ja17_15{
    public static  void main(String[] args){
        ArrayList<String> al=new ArrayList<String>(new TextFile("Ja17_14.java","\\W+"));
        Iterator<String> it=al.iterator();
        SlowMap<String,Integer> lhm=new SlowMap<String,Integer>();
        Integer i;
        while(it.hasNext()){
            String mm=it.next();
            i=lhm.get(mm);
            lhm.put(mm,i==null?1:++i);
        }
        print(lhm);
    }
}




練習16:

// Apply the tests in Maps.java to SlowMap to verify that it works. 
// Fix anything in SlowMap that doesn't work properly. 
//: containers/Maps.java
// Things you can do with Maps.
import java.util.concurrent.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
import containers.*;
class SlowMap<K,V> extends AbstractMap<K,V> {
  private List<K> keys = new ArrayList<K>();
  private List<V> values = new ArrayList<V>();
  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;
  }
  public V get(Object key) { // key is type Object, not K
    if(!keys.contains(key))
      return null;
    return values.get(keys.indexOf(key));
  }
  public EntrySet entires=new EntrySet();
  public EntrySet entrySet(){return new EntrySet();}

  private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    /*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 int size(){return keys.size();}
    public Iterator<Map.Entry<K,V>> iterator(){
        return new Iterator<Map.Entry<K,V>>(){
            private int index=-1;
            public boolean hasNext(){return index<keys.size()-1;}
            @SuppressWarnings("unchecked")
            public Map.Entry<K,V> next(){
                int i=++index;
                return new MapEntry(keys.get(i),values.get(i));
            }
            public void remove(){keys.remove(index--);}
        };
      }
  }
  public static void main(String[] args) {
    SlowMap<String,String> m= new SlowMap<String,String>();
    m.putAll(Countries.capitals(15));
    System.out.println(m);
    System.out.println(m.get("BULGARIA"));
    System.out.println(m.entrySet());
  }
}
public class Ja17_16 {
  public static void printKeys(Map<Integer,String> map) {
    printnb("Size = " + map.size() + ", ");
    printnb("Keys: ");
    print(map.keySet()); // Produce a Set of the keys
  }
  public static void test(Map<Integer,String> map) {
    print(map.getClass().getSimpleName());
    map.putAll(new CountingMapData(25));
    // Map has 'Set' behavior for keys:
    map.putAll(new CountingMapData(25));
    printKeys(map);
    // Producing a Collection of the values:
    printnb("Values: ");
    print(map.values());
    print(map);
    print("map.containsKey(11): " + map.containsKey(11));
    print("map.get(11): " + map.get(11));
    print("map.containsValue(\"F0\"): "
      + map.containsValue("F0"));
    Integer key = map.keySet().iterator().next();
    print("First key in map: " + key);
    map.remove(key);
    printKeys(map);
    map.clear();
    print("map.isEmpty(): " + map.isEmpty());
    map.putAll(new CountingMapData(25));
    // Operations on the Set change the Map:
    map.keySet().removeAll(map.keySet());
    print("map.isEmpty(): " + map.isEmpty());
  }
  public static void main(String[] args) {
    test(new HashMap<Integer,String>());
    test(new SlowMap<Integer,String>());
  }
} 


練習17:
// Implement the rest of the Map interface for SlowMap.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
import containers.*;

public class Ja17_17<K,V> implements Map<K,V> {
	private List<K> keys = new ArrayList<K>();	
	private List<V> values = new ArrayList<V>();
	private EntrySet entries = new EntrySet();
	private Set<K> keySet = new KeySet();	
	public Set<Map.Entry<K,V>> entrySet() { return entries; }
	public Set<K> keySet() { return keySet; }
	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;
	}
	public V get(Object key) { // key is type Object, not K
		if(!keys.contains(key))
			return null;
		return values.get(keys.indexOf(key));
	}
	private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
		public int size() { return keys.size(); }
		public Iterator<Map.Entry<K,V>> iterator() {
			return new Iterator<Map.Entry<K,V>>() {
				private int index = -1;
				public boolean hasNext() {
					return index < keys.size() - 1; 
				}
				@SuppressWarnings("unchecked")
				public Map.Entry<K,V> next() {
					int i = ++index;
					return new MapEntry(
					keys.get(i), values.get(i));	 
				}
				public void remove() {
					keys.remove(index);
    					values.remove(index--);		
				}				
			};
		}				
	}
	public void clear() {
		keys.clear();
		values.clear();
	}
	public boolean containsKey(Object key) {
		return keys.contains(key);
	}
	public boolean containsValue(Object value) {
		return values.contains(value);
	}
	public boolean equals(Object o) {
		if(o instanceof Ja17_17) {
			if(this.entrySet().equals(((Ja17_17)o).entrySet()))
				return true;
		}
		return false;
	}
	public int hashCode() {
		return this.entrySet().hashCode();
	}	
	public boolean isEmpty() {
		return this.entrySet().isEmpty();
	}	
	private class KeySet extends AbstractSet<K> {
		public int size() { return keys.size(); }
		public Iterator<K> iterator() {
			return new Iterator<K>() {
				private int index = -1;
				public boolean hasNext() {
					return index < keys.size() - 1; 
				}
				public K next() {
					int i = ++index;
					return keys.get(index);	 
				}
				public void remove() {
					keys.remove(index--);
    				}				
			};
		}
		
	}
	public void putAll(Map<? extends K,? extends V> m) {
		for(Map.Entry<? extends K,? extends V> me : m.entrySet())
			this.put(me.getKey(), me.getValue());
	}
	public V remove(Object key) {
		V v = this.get(key);	
		int i = keys.indexOf(key);
		keys.remove(i);
		values.remove(i);
		return v;
	}
	public int size() { return keys.size(); }
	public Collection<V> values() { 
		return values;
	}
	public String toString() {
		return this.entrySet().toString();
	}	
	public static void main(String[] args) {
		Ja17_17<String,String> m = new Ja17_17<String,String>();
		m.putAll(Countries.capitals(15));
		print("m: " + m);
		print("m.get(\"BURUNDI\"): " + m.get("BURUNDI"));
		print("m.entrySet(): " + m.entrySet());
		print("m.keySet(): " + m.keySet());		
		print("m.values() = " + m.values());
		print("Two different maps: ");
		Ja17_17<String,String> m2 = new Ja17_17<String,String>();
		print("m.equals(m2): " + m.equals(m2));
		m2.putAll(Countries.capitals(15));
		print("Maps with same entries: ");
		print("m.equals(m2): " + m.equals(m2));
		m.clear();
		print("After m.clear(), m.isEmpty(): " + 
			m.isEmpty() + ", m = " + m);
		m2.keySet().clear();
		print("After m2.keySet().clear(), m2.isEmpty(): " 
			+ m2.isEmpty() + ", m2 = " + m2);
	}
}



練習18:
// Using SlowMap.java for inspiration, create a SlowSet.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class Ja17_18<E> implements Set<E> {
	private List<E> elements = new ArrayList<E>();	
	public boolean add(E e) {
		if(elements.contains(e)) return false;
		elements.add(e);
		return true;
	}
	public boolean addAll(Collection<? extends E> c) {
		return(elements.addAll(c));
	}	
	public void clear() {
		elements.clear();
	}
	public boolean contains(Object o) {
		return elements.contains(o);
	}
	public boolean containsAll(Collection<?> c) {
		int count = 0; 
		for(Object o : c)
			if(elements.contains(o)) count++;
		if(count == c.size()) return true;
		return false;		
	}
	public boolean equals(Object o) {
		if(o instanceof Ja17_18) {
			if((elements.size() == ((Ja17_18)o).size())) {
				int count = 0;
                for(int i = 0; i < elements.size(); i++)
                    if(elements.get(i).equals(((Ja17_18)o).elements.get(i)))
						count++;
				if(count == elements.size()) return true;
			}					
		}
		return false;		
	}
	public int hashCode() {
		int result = 0;
		for(int i = 0; i < elements.size(); i++)
			result += elements.get(i).hashCode();
		return result;
	}	
	public boolean isEmpty() {
		return(elements.size() == 0);
	}
	public Iterator<E> iterator() {
		return elements.iterator();
	}
	public boolean remove(Object o) {
		return(elements.remove(o));
	}
	public boolean removeAll(Collection<?> c) {
		int n = elements.size();
		for(Object o : c) elements.remove(o);
		if(n != elements.size()) return true;
		return false;
	}
	public boolean retainAll(Collection<?> c) {
		int n = elements.size();
		for(int i = 0; i < elements.size(); i++) { 
			E e = elements.get(i);
			if(!(c.contains(e))) elements.remove(e);
		}
		if(n != elements.size()) return true;
		return false;
	}
	public int size() { return elements.size(); }
	public Object[] toArray() {
		return elements.toArray();
	}
	public <T> T[] toArray(T[] a) {
		return elements.toArray(a);
	}
	
	public String toString() {
		if(elements.size() == 0) return "[]";
		StringBuilder s = new StringBuilder();
		s.append("[");
		for(int i = 0; i < elements.size() - 1; i++)
			s.append(String.valueOf(elements.get(i)) + ", ");
		s.append(String.valueOf(elements.get(elements.size() -1)));
		s.append("]");
		return s.toString();
	}
	
	public static void main(String[] args) {
		Ja17_18<String> ss = new Ja17_18<String>();
		ss.add("hi");
		print(ss);
		ss.add("there");
		print(ss);		
		List<String> list = Arrays.asList("you", "cutie", "pie");
		ss.addAll(list);
		print(ss);
		print("ss.size() = " + ss.size());
		print("ss.contains(\"you\"): " + ss.contains("you"));
		print("ss.contains(\"me\"): " + ss.contains("me"));	
		print("ss.containsAll(list): " + ss.containsAll(list));
		Ja17_18<String> ss2 = new Ja17_18<String>();
		print("ss2 = " + ss2);
		print("ss.containsAll(ss2): " + ss.containsAll(ss2));
		print("ss2.containAll(ss): " + ss2.containsAll(ss));
		ss2.add("you");
		ss2.add("cutie");
		ss.removeAll(ss2);
		print("ss = " + ss);
		print("ss.hashCode() = " + ss.hashCode());
		List<String> list2 = Arrays.asList("hi", "there", "pie");
		ss2.remove("you");
		print(ss2);
		print("ss2.isEmpty(): " + ss2.isEmpty());
		ss2.clear();
		print("ss2.isEmpty(): " + ss2.isEmpty());
		ss2.addAll(list2);
		print("ss2 = " + ss2);
		print("ss.equals(ss2): " + ss.equals(ss2));
		String[] sa = new String[3];
		print("ss.toArray(sa): " + ss.toArray(sa));
		for(int i = 0; i < sa.length; i++) printnb(sa[i] + " " );				
	}
}




練習19:
// Repeat Exercise 13 using a SimpleHashMap.

import net.mindview.util.*;
import static net.mindview.util.Print.*;
import java.util.*;
import containers.*;
public class Ja17_19{
    public static  void main(String[] args){
        ArrayList<String> al=new ArrayList<String>(new TextFile("Ja17_12.java","\\W+"));
        Iterator<String> it=al.iterator();
        SimpleHashMap<String,Integer> lhm=new SimpleHashMap<String,Integer>();
        Integer i;
        while(it.hasNext()){
            String mm=it.next();
            i=lhm.get(mm);
            lhm.put(mm,i==null?1:++i);
        }
        print(lhm);
    }
}




練習20:

// Modify SimpleHashMap so that it reports collisions, and test
// this by adding the same data twice so that you see collisions.
import java.util.*;
import containers.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class SimpleHashMap<K,V> extends AbstractMap<K,V> {
  // Choose a prime number for the hash table
  // size, to achieve a uniform distribution:
  static final int SIZE = 997;
  // You can't have a physical array of generics,
  // but you can upcast to one:
  @SuppressWarnings("unchecked")
  LinkedList<MapEntry<K,V>>[] buckets = new LinkedList[SIZE];
  public V put(K key, V value) {
    V oldValue = null;
    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<K,V> pair = new MapEntry<K,V>(key, value);
    boolean found = false;
    ListIterator<MapEntry<K,V>> it = bucket.listIterator();
    while(it.hasNext()) {
      MapEntry<K,V> iPair = it.next();
      if(iPair.getKey().equals(key)) {
          print("collisions!collisions!");
        oldValue = iPair.getValue();
        it.set(pair); // Replace old with new
        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<Map.Entry<K,V>>();
    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<String,String>();
    m.putAll(Countries.capitals(25));
    System.out.println(m);
    System.out.println(m.get("ERITREA"));
    System.out.println(m.entrySet());
  }
}

public class Ja17_20{
    public static  void main(String[] args){
        SimpleHashMap<String,Integer> sh=new SimpleHashMap<String,Integer>();
        sh.put("aa",1);
        sh.put("aa",4);
    }
}


練習21:
/* Modify SimpleHashMap.java to that it reports the number of
* "probes" necessary when collisions occur. That is, how 
* many calls to next() must be made on the Iterators that
* walk the LinkedLists looking for matches?
*/
import java.util.*;
import containers.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class SimpleHashMap<K,V> extends AbstractMap<K,V> {
  // Choose a prime number for the hash table
  // size, to achieve a uniform distribution:
  static final int SIZE = 997;
  // You can't have a physical array of generics,
  // but you can upcast to one:
  @SuppressWarnings("unchecked")
  LinkedList<MapEntry<K,V>>[] buckets = new LinkedList[SIZE];
  public V put(K key, V value) {
    V oldValue = null;
    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<K,V> pair = new MapEntry<K,V>(key, value);
    boolean found = false;
    ListIterator<MapEntry<K,V>> it = bucket.listIterator();
    int count=0;
    while(it.hasNext()) {
      MapEntry<K,V> iPair = it.next();
      count++;
      if(iPair.getKey().equals(key)) {
          print("collisions!collisions!: "+count);
        oldValue = iPair.getValue();
        it.set(pair); // Replace old with new
        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<Map.Entry<K,V>>();
    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<String,String>();
    m.putAll(Countries.capitals(25));
    System.out.println(m);
    System.out.println(m.get("ERITREA"));
    System.out.println(m.entrySet());
  }
}

public class Ja17_21{
    public static  void main(String[] args){
        SimpleHashMap<String,Integer> sh=new SimpleHashMap<String,Integer>();
        sh.put("aa",1);
        sh.put("bb",1);
        sh.put("cc",1);
        sh.put("ee",1);
        sh.put("ff",1);
        sh.put("mm",1);
        sh.put("ee",4);
    }
}




練習22:
// Implement the clear() and remove() methods for SimpleHashMap.
import java.util.*;
import containers.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class SimpleHashMap<K,V> extends AbstractMap<K,V> {
  // Choose a prime number for the hash table
  // size, to achieve a uniform distribution:
  static final int SIZE = 997;
  // You can't have a physical array of generics,
  // but you can upcast to one:
  @SuppressWarnings("unchecked")
  LinkedList<MapEntry<K,V>>[] buckets = new LinkedList[SIZE];
  public V remove(Object o){
      V v=null;
      if(this.get(o)!=null){
          int index=Math.abs(o.hashCode())%SIZE;
          for(MapEntry<K,V> ipair:buckets[index])
              if(ipair.getKey().equals(o)){
                  int i=buckets[index].indexOf(ipair);
                  buckets[index].remove(i);
                  v=ipair.getValue();
                  break;
              }
      }
      return v;
  }
  public void clear(){
    for(LinkedList<MapEntry<K,V>> b:buckets){
        if(b!=null)b.clear();
    }
  }
  public V put(K key, V value) {
    V oldValue = null;
    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<K,V> pair = new MapEntry<K,V>(key, value);
    boolean found = false;
    ListIterator<MapEntry<K,V>> it = bucket.listIterator();
    int count=0;
    while(it.hasNext()) {
      MapEntry<K,V> iPair = it.next();
      count++;
      if(iPair.getKey().equals(key)) {
          print("collisions!collisions!: "+count);
        oldValue = iPair.getValue();
        it.set(pair); // Replace old with new
        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<Map.Entry<K,V>>();
    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<String,String>();
    m.putAll(Countries.capitals(25));
    System.out.println(m);
    System.out.println(m.get("ERITREA"));
    System.out.println(m.entrySet());
  }
}

public class Ja17_22{
    public static  void main(String[] args){
        SimpleHashMap<String,Integer> sh=new SimpleHashMap<String,Integer>();
        sh.put("aa",1);
        sh.put("bb",1);
        sh.put("cc",1);
        sh.put("ee",1);
        sh.put("ff",1);
        sh.put("mm",1);
        sh.put("ee",4);
        print(sh.remove("ee"));
        print(sh);
        sh.clear();
        print(sh);
    }
}





練習23:
// Implement the rest of the Map interface for SimpleHashMap.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class SimpleHashMap23<K,V> implements Map<K,V> {
	// Choose a prime number for the hash table
	// size, to achieve a uniform distribution:
	static final int SIZE = 997;
	// You can't have a physical array of generics,
	// but you can upcast to one:
	@SuppressWarnings("unchecked")
	LinkedList<MapEntry<K,V>>[] buckets = new LinkedList[SIZE];
	public int size() {
		int result = 0;
		for(LinkedList bucket : buckets)
			if(bucket != null) result += bucket.size();
		return result;
	}
	// Three methods to help with proper iteration by EntrySet.iterator():  
	private int firstNonEmptyBucket() {
		if(buckets.length < 1) return -1;
		for(int j = 0; j < buckets.length; j++) 
			if(buckets[j] != null) return j;
		return -1;		
	}
	private int start(int i) {
		int first = this.firstNonEmptyBucket();
		if(i < first) return -1;
		if(i == first) return 0;
		int result = 0;
		for(int j = first; j < i; j++) 
			if(buckets[j] != null) result += buckets[j].size();
		return result;
	}
	private int end(int i) {
		int first = this.firstNonEmptyBucket();
		if(i < first) return -1;
		return start(i) + ((buckets[i] == null) ? 0 : buckets[i].size()); 
	}
	private EntrySet entries = new EntrySet();
	private KeySet keys = new KeySet();
	public Set<Map.Entry<K,V>> entrySet() { return entries; }
	public Set<K> keySet() { return keys; }
	public boolean isEmpty() {
		return this.size() == 0;
	}	
	public V put(K key, V value) {
		V oldValue = null;
		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<K,V> pair = new MapEntry<K,V>(key, value);
		boolean found = false;
		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); // Replace old with new
				found = true;
				break;
			}
		}	
		if(!found)
			buckets[index].add(pair);
		return oldValue;
	}
	public void putAll(Map<? extends K, ? extends V> m) {
		for(Map.Entry<? extends K,? extends V> me : m.entrySet())
			this.put(me.getKey(), me.getValue());
	}
	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 void clear() {
		for(LinkedList<MapEntry<K,V>> bucket : buckets)
			if(bucket != null) bucket.clear();		
	}
	public boolean containsKey(Object key) {
		int index = Math.abs(key.hashCode()) % SIZE;
		if(buckets[index] == null) return false;
		for(MapEntry<K,V> iPair : buckets[index])
			if(iPair.getKey().equals(key))
				return true;
		return false;
	}	
	public boolean containsValue(Object value) {
		for(LinkedList<MapEntry<K,V>> bucket : buckets) {
			if(bucket != null) 
				for(MapEntry<K,V> iPair : bucket)
					if(iPair.getValue().equals(value)) return true;
		}
		return false;
	}
	public Collection<V> values() {
		HashSet<V> vals = new HashSet<V>();
		for(LinkedList<MapEntry<K,V>> bucket : buckets) {
			if(bucket != null) 
				for(MapEntry<K,V> iPair : bucket)
					vals.add(iPair.getValue()); 
		}
		return vals;
	}
	public boolean equals(Object o) {
		if(o instanceof SimpleHashMap23) {
			if(this.entrySet().equals(((SimpleHashMap23)o).entrySet()))
				return true;
		}
		return false;
	}
	public int hashCode() {
		return this.entrySet().hashCode();
	}	
	public V remove(Object o) {
		V v = null;
		if(this.get(o) != null) {
			int index = Math.abs(o.hashCode()) % SIZE;
			for(MapEntry<K,V> iPair : buckets[index])
				if(iPair.getKey().equals(o)) {
					v = iPair.getValue();
					int i =
					buckets[index].indexOf(iPair);
					buckets[index].remove(i);
					break;		
				}
		}
		return v;		
	}
	private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
		public int size() { return SimpleHashMap23.this.size(); }
		public Iterator<Map.Entry<K,V>> iterator() {
			return new Iterator<Map.Entry<K,V>>() {
				private int index = -1;
				public boolean hasNext() {
					return index < SimpleHashMap23.this.size() - 1; 
				}
				@SuppressWarnings("unchecked")
				public Map.Entry<K,V> next() {
					int i = ++index;
					for(int j = 0; j < SIZE; j++) {
						if((start(j) <= index) && (index < end(j)))
							return new MapEntry(
							buckets[j].get(index - start(j)).getKey(),
							buckets[j].get(index - start(j)).getValue());	
					}
					return null;						
				}
				public void remove() {
					for(int j = 0; j < SIZE; j++)
						if(start(j) <= index && index < end(j))
							buckets[j].remove(index - start(j));
					index--;			
				}				
			};
		}
	}	
	private class KeySet extends AbstractSet<K> {
		public int size() { return SimpleHashMap23.this.size(); }
		public Iterator<K> iterator() {
			return new Iterator<K>() {
				private int index = -1;
				public boolean hasNext() {
					return index < SimpleHashMap23.this.size() - 1; 
				}
				public K next() {
					int i = ++index;
					for(int j = 0; j < SIZE; j++) {
						if((start(j) <= index) && (index < end(j)))
							return buckets[j].get(index - start(j)).getKey();
					}
					return null; 	
				}
				public void remove() {
					for(int j = 0; j < SIZE; j++)
						if(start(j) <= index && index < end(j))
							buckets[j].remove(index - start(j));
					index--;			
				}
			};
		}
	} 
	public String toString() {
		return this.entrySet().toString();
	}	
	public static void main(String[] args) {
		SimpleHashMap23<String,String> map =
			new SimpleHashMap23<String,String>();
		map.putAll(Countries.capitals(3));
		print("map = " + map);
		print("map.entrySet(): " + map.entrySet());
		print("map.keySet(): " + map.keySet());
		print("map.values() = " + map.values());
		print("map.isEmpty(): " + map.isEmpty());
		print("map.containsKey(\"ALGERIA\"): " + map.containsKey("ALGERIA"));
		print("map.containsValue(\"Algiers\"): " + map.containsValue("Algiers"));
		print("map.get(\"ALGERIA\"): " + map.get("ALGERIA"));
		print("map.remove(\"ALGERIA\"): " + map.remove("ALGERIA"));
		print("After map.remove(\"ALGERIA\"), map.containsKey(\"ALGERIA\"): " +
			map.containsKey("ALGERIA"));
		print(" and map.get(\"ALGERIA\"): " + map.get("ALGERIA"));
		print(" and map: = " + map);	
		map.clear();
		print("After map.clear(), map = " + map);
		print(" and map.isEmpty(): " + map.isEmpty());
		map.putAll(Countries.capitals(3));
		print("After map.putAll(Countries.capitals(3)), map = " + map);
		SimpleHashMap23<String,String> map2 = 
			new SimpleHashMap23<String,String>();
		map2.putAll(Countries.capitals(4));
		print("After map2.putAll(Countries.capitals(4)), map2 = " + map2);
		print(" and map.equals(map2): " + map.equals(map2));
		map2.remove("BOTSWANA");	
		print("After map2.remove(\"BOTSWANT\"), map.equals(map2): " + map.equals(map2));
		map.entrySet().clear();
		print("After map.entrySet().clear, map = " + map);
		map.putAll(Countries.capitals(3));
		print("After map.putAll(Countries.capitals(3)), map = " + map);
		map.keySet().clear();
		print("After map.keySet().clear(), map = " + map);				
	}
}



練習24:
// Following the example in SimpleHashMap.java, 
// create and test a SimpleHashSet.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class SimpleHashSet24<E> implements Set<E> {
	static final int SIZE = 997;
	@SuppressWarnings("unchecked")
	LinkedList<E>[] buckets = new LinkedList[SIZE];
    public int size(){
        int result=0;
        for(LinkedList<E> bucket:buckets){
            if(bucket!=null)result+=bucket.size();
        }
        return result;
    }
    public boolean add(E e){
        if(this.contains(e))return false;
        int index=e.hashCode()%SIZE;
        if(buckets[index]==null)buckets[index]=new LinkedList<E>();
        buckets[index].add(e);
        return true;
    }
	public boolean addAll(Collection<? extends E> c) {
		int start = this.size();
		for(E e : c) this.add(e);
		return(this.size() > start);		
	}	
	public void clear() {
		for(LinkedList<E> bucket : buckets)
			if(bucket != null) bucket.clear();		
	}
	public boolean contains(Object o) {
		int index = Math.abs(o.hashCode()) % SIZE;
		if(buckets[index] == null) return false;
		for(E e : buckets[index])
			if(e.equals(o)) return true;
		return false;
	}	
	public boolean containsAll(Collection<?> c) {
		int count = 0; 
		for(Object o : c)
			if(this.contains(o)) count++;
		if(count == c.size()) return true;
		return false;		
	}
	public boolean equals(Object o) {
		if(o instanceof SimpleHashSet24) {
			if((this.size() == ((SimpleHashSet24)o).size())) {
				int count = 0;
				Iterator it = ((SimpleHashSet24)o).iterator();
				while(it.hasNext())
					if(this.contains(it.next())) count++;
				if(count == this.size()) return true;
			}					
		}
		return false;		
	}
    public int hashCode(){
        int result=0;
        for(LinkedList<E> bucket:buckets){
            if(bucket!=null)
                for(E e:bucket){
                    if(e!=null)result+=e.hashCode();
                }
        }
        return result;
    }
	public boolean isEmpty() {
		return(this.size() == 0);
	}
	// Three methods to help with proper iteration by SimpleHashSet.iterator():  
	private int firstNonEmptyBucket() {
		if(buckets.length < 1) return -1;
		for(int j = 0; j < buckets.length; j++) 
			if(buckets[j] != null) return j;
		return -1;		
	}
	private int start(int i) {
		int first = this.firstNonEmptyBucket();
		if(i < first) return -1;
		if(i == first) return 0;
		int result = 0;
		for(int j = first; j < i; j++) 
			if(buckets[j] != null) result += buckets[j].size();
		return result;
	}
	private int end(int i) {
		int first = this.firstNonEmptyBucket();
		if(i < first) return -1;
		return start(i) + ((buckets[i] == null) ? 0 : buckets[i].size()); 
	}
	public Iterator<E> iterator() {
		return new Iterator<E>() {
			private int index = -1;
			public boolean hasNext() {
				return index < SimpleHashSet24.this.size() - 1; 
			}
			public E next() {
				int i = ++index;
				for(int j = 0; j < SIZE; j++) {
					if((start(j) <= index) && (index < end(j)))
						return buckets[j].get(index - start(j));
				}
				return null; 	
			}
			public void remove() {
				for(int j = 0; j < SIZE; j++)
					if(start(j) <= index && index < end(j))
						buckets[j].remove(index - start(j));
				index--;			
			}
		};
	}
	public boolean remove(Object o) {
		int index = Math.abs(o.hashCode()) % SIZE;
		if(buckets[index] == null) return false;
		for(E e : buckets[index]) {
			if(e.equals(o)) {
				buckets[index].remove(e); 
				return true;
			}
		}
		return false;
	}	
	public boolean removeAll(Collection<?> c) {
		int n = this.size();
		for(Object o : c) this.remove(o);
		if(n != this.size()) return true;
		return false;
	}
	public boolean retainAll(Collection<?> c) {
		int n = this.size();
		for(LinkedList<E> bucket : buckets) {
			for(E e : bucket) 
				if(!(c.contains(e))) this.remove(e);
		}
		if(n != this.size()) return true;
		return false;
	}	
	public Object[] toArray() {
		Object[] result = new Object[this.size()];
		Iterator<E> it = this.iterator();
		for(int i = 0; i < this.size(); i++) 
			result[i] = it.next(); 			
		return result;
	}
	@SuppressWarnings("unchecked")	
	public <T> T[] toArray(T[] a) {
		Iterator<E> it = this.iterator();
		for(int i = 0; i < this.size(); i++) {
			E x = it.next();
				try {
			 		a[i] = (T)x;
				} catch(ClassCastException e) {
					throw new RuntimeException(e);
				}
		}
		return a;
	}
	public String toString() {
		if(this.size() == 0) return "[]";
		StringBuilder s = new StringBuilder();
		s.append("[");
		for(LinkedList<E> bucket : buckets) {
			if(bucket != null)
				for(E e : bucket) 
					s.append(String.valueOf(e) + " ");				
		}		
		s.replace(s.length() - 1, s.length(), "]");
		return s.toString();
	}	
}
public class Ja17_24{
	public static void main(String[] args) {
		SimpleHashSet24<String> shs = new SimpleHashSet24<String>();
		print("New empty SimpleHashSet24, shs = " + shs);
		shs.add("hi");
		shs.add("there");
		print("After shs.add(\"hi\") and shs.add(\"there\"), shs = " + shs);			
		List<String> list = Arrays.asList("you", "cutie", "pie");
		shs.addAll(list);
		print("After shs.addAll(list) (you, cutie, pie), shs = " + shs);
		print("shs.size() = " + shs.size());
		print("shs.contains(\"you\"): " + shs.contains("you"));
		print("shs.contains(\"me\"): " + shs.contains("me"));	
		print("shs.containsAll(list): " + shs.containsAll(list));
		SimpleHashSet24<String> shs2 = new SimpleHashSet24<String>();
		print("New empty shs2 = " + shs2);		
		print("shs.containsAll(list): " + shs.containsAll(list));
		print("shs2.containAll(list): " + shs2.containsAll(list));
		print("shs.containsAll(shs2): " + shs.containsAll(shs2));
		print("shs2.containAll(shs2): " + shs2.containsAll(shs2));
		shs2.add("you");
		shs2.add("cutie");
		print("After shs2.add(\"you\"), shs2.add(\"cutie\"), shs2 = " + shs2);
		shs.removeAll(shs2);
		print("After shs.removeAll(shs2), shs = " + shs);
		print("shs.hashCode() = " + shs.hashCode());
		print("shs2 = " + shs2);
		print("shs2.isEmpty(): " + shs2.isEmpty());
		shs2.clear();
		print("After shs2.clear(), shs2.isEmpty(): " + shs2.isEmpty());
		List<String> list2 = Arrays.asList("hi", "there", "pie");
		shs2.addAll(list2);
		print("After shs2.addAll(list2) (hi, there, pie), shs2 = " + shs2);
		print("shs.equals(shs2): " + shs.equals(shs2));
		String[] sa = new String[3];
		shs.toArray(sa);
		printnb("After String[] sa = new String[3], shs.toArray(sa), sa holds: "); 
		for(int i = 0; i < sa.length; i++) printnb(sa[i] + " " );
	}
}



練習25:
/* Instead of using a ListIterator for each bucket, modify MapEntry so that
* it is a self-contained singly linked list (each MapEntry should have a
* forward link to the next MapEntry). Modify the rest of the code in 
* SimpleHashMap.java so that this new approach works correctly.
*/
import java.util.*;
import containers.*;
import net.mindview.util.*;

class SimpleHashMap<K,V> extends AbstractMap<K,V> {
	class MapEntry25<K,V> implements Map.Entry<K,V> {
		private K key;
		private V value;
		private MapEntry25<K,V> nextEntry = null;
		public MapEntry25(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;
		}
		public MapEntry25<K,V> getNextEntry() {
			return this.nextEntry;
		}
		public void setNextEntry(MapEntry25<K,V> nextEntry) {
			this.nextEntry = nextEntry;
		}
		public int hashCode() {
			return (key == null ? 0 : key.hashCode()) ^
				(value == null ? 0 : value.hashCode());
		}
		public boolean equals(Object o) {
			if(!(o instanceof MapEntry25)) return false;
			MapEntry25 me = (MapEntry25)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; }
	}
	// Choose a prime number for the hash table
	// size, to achieve a uniform distribution:
	static final int SIZE = 997;
	// You can't have a physical array of generics,
	// but you can upcast to one:
	@SuppressWarnings("unchecked")
	LinkedList<MapEntry25<K,V>>[] buckets =
		new LinkedList[SIZE];
	public V put(K key, V value) {
		V oldValue = null;
		MapEntry25<K,V> pair = new MapEntry25<K,V>(key, value);
		int index = Math.abs(key.hashCode()) % SIZE;
		if(buckets[index] == null) {
			buckets[index] = new LinkedList<MapEntry25<K,V>>();
			LinkedList<MapEntry25<K,V>> bucket = buckets[index];
			bucket.add(pair);
		}
		LinkedList<MapEntry25<K,V>> bucket = buckets[index];
		if(buckets[index].size() > 0) {
			for(MapEntry25<K,V> entry = bucket.get(0); entry != null; 
			entry = entry.getNextEntry()) {
				if(entry.getKey().equals(key)) {
				oldValue = entry.getValue();
				entry.setValue(value);
				return oldValue;
				}
				}
			bucket.add(pair);
			int i = bucket.indexOf(pair); 
			if(i > 0) bucket.get(i - 1).setNextEntry(pair);
			return pair.getValue();	
		}
		return oldValue;	
	}
	public V get(Object key) {
		int index = Math.abs(key.hashCode()) % SIZE;
		if(buckets[index] == null) return null;
		for(MapEntry25<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<Map.Entry<K,V>>();
		for(LinkedList<MapEntry25<K,V>> bucket : buckets) {
			if(bucket == null) continue;
			for(MapEntry25<K,V> mpair : bucket)
				set.add(mpair);
		}
		return set;
	}
}

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;
  }
  public int hashCode() {
    return (key==null ? 0 : key.hashCode()) ^
      (value==null ? 0 : value.hashCode());
  }
  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; }
} ///:~

public class Ja17_25{
	public static void main(String[] args) {
		SimpleHashMap<String,String> m =
			new SimpleHashMap<String,String>();
		m.putAll(Countries.capitals(5));
		System.out.println(m);
		System.out.println(m.put("BENIN","New York?"));		
		System.out.println(m.put("BENIN","Porto-Novo"));
		System.out.println(m.get("BENIN"));
		System.out.println(m.entrySet());
	}
}



練習26:
/* Add a char field to CountedString that is also initialized in the 
* constructor; and modify the hashCode() and equals() methods to 
* include the value of this char.
*/
import java.util.*;
import static net.mindview.util.Print.*;

class CountedString {
  private static List<String> created =
    new ArrayList<String>();
  private String s;
  private char ch;
  private int id = 0;
  public CountedString(String str) {
    s = str;
    created.add(s);
    // id is the total number of instances
    // of this string in use by CountedString:
    for(String s2 : created)
      if(s2.equals(s))
      {id++;ch=(char)(id/2);}
  }
  public String toString() {
    return "String: " + s + " id: " + id +
      " hashCode(): " + hashCode();
  }
  public int hashCode() {
    // The very simple approach:
    // return s.hashCode() * id;
    // Using Joshua Bloch's recipe:
    int result = 17;
    result = 37 * result + s.hashCode();
    result = 37 * result + id;
    result = 37 * result + (int)ch;

    return result;
  }
  public boolean equals(Object o) {
    return o instanceof CountedString &&
      s.equals(((CountedString)o).s) &&
      id == ((CountedString)o).id;
  }
}
public class Ja17_26{
  public static void main(String[] args) {
    Map<CountedString,Integer> map =
      new HashMap<CountedString,Integer>();
    CountedString[] cs = new CountedString[5];
    for(int i = 0; i < cs.length; i++) {
      cs[i] = new CountedString("hi");
      map.put(cs[i], i); // Autobox int -> Integer
    }
    print(map);
    for(CountedString cstring : cs) {
      print("Looking up " + cstring);
      print(map.get(cstring));
    }
  }

}



練習27:
/* Modify the hashCode() in CountedString.java by removing the combination
* with id, and demonstrate that CountedString still works as a key. What
* is the problem with this approach?
*/
/* Add a char field to CountedString that is also initialized in the 
* constructor; and modify the hashCode() and equals() methods to 
* include the value of this char.
*/
import java.util.*;
import static net.mindview.util.Print.*;

class CountedString {
  private static List<String> created =
    new ArrayList<String>();
  private String s;
  private int id = 0;
  public CountedString(String str) {
    s = str;
    created.add(s);
    // id is the total number of instances
    // of this string in use by CountedString:
    for(String s2 : created)
      if(s2.equals(s))
        id++;
  }
  public String toString() {
    return "String: " + s + " id: " + id +
      " hashCode(): " + hashCode();
  }
  public int hashCode() {
    // The very simple approach:
    // return s.hashCode() * id;
    // Using Joshua Bloch's recipe:
    int result = 17;
    result = 37 * result + s.hashCode();
    return result;
  }
  public boolean equals(Object o) {
    return o instanceof CountedString &&
      s.equals(((CountedString)o).s) &&
      id == ((CountedString)o).id;
  }
}
public class Ja17_27{
  public static void main(String[] args) {
    Map<CountedString,Integer> map =
      new HashMap<CountedString,Integer>();
    CountedString[] cs = new CountedString[5];
    for(int i = 0; i < cs.length; i++) {
      cs[i] = new CountedString("hi");
      map.put(cs[i], i); // Autobox int -> Integer
    }
    print(map);
    for(CountedString cstring : cs) {
      print("Looking up " + cstring);
      print(map.get(cstring));
    }
  }

}




練習28:
/* Modify net/mindview/util/Tuple.java to make it a 
* general-purpose class by adding hashCode(), equals(), 
* and implementing Comparable for each type of Tuple.
*/
import net.mindview.util.*;
class TwoTuple<A,B> implements Comparable{
  public final A first;
  public final B second;
  public TwoTuple(A a, B b) { first = a; second = b; }
  public String toString() {
    return "(" + first + ", " + second + ")";
  }
  public int hashCode(){
    int result=17;
    result=result*37+first.hashCode();
    result=result*37+second.hashCode();
    return result;
  }
    public boolean equals(Object o){
        if(o.getClass().getSimpleName()==getClass().getSimpleName()){
            TwoTuple to=(TwoTuple)o;
            if(first.equals((TwoTuple)to.first))
                if(second.equals((TwoTuple)to.second))
                    return true;
        }
        return false;
    }
    public int compareTo(Object o){
        int c1=(o.getClass().getSimpleName().compareTo(getClass().getSimpleName()));
        if(c1!=0)return c1;
        TwoTuple to=(TwoTuple)o;
        return ((this.hashCode()<to.hashCode())?-1:(this.hashCode()==to.hashCode()?0:1));
    }
}
class ThreeTuple<A,B,C> extends TwoTuple<A,B> {
  public final C third;
  public ThreeTuple(A a, B b, C c) {
    super(a, b);
    third = c;
  }
  public String toString() {
    return "(" + first + ", " + second + ", " + third +")";
  }
}
class Tuple {
  public static <A,B> TwoTuple<A,B> tuple(A a, B b) {
    return new TwoTuple<A,B>(a, b);
  }
  public static <A,B,C> ThreeTuple<A,B,C>
  tuple(A a, B b, C c) {
    return new ThreeTuple<A,B,C>(a, b, c);
  }
  public static <A,B,C,D> FourTuple<A,B,C,D>
  tuple(A a, B b, C c, D d) {
    return new FourTuple<A,B,C,D>(a, b, c, d);
  }
  public static <A,B,C,D,E>
  FiveTuple<A,B,C,D,E> tuple(A a, B b, C c, D d, E e) {
    return new FiveTuple<A,B,C,D,E>(a, b, c, d, e);
  }
  public static <A,B,C,D,E,F>
  SixTuple<A,B,C,D,E,F> tuple(A a, B b, C c, D d, E e,F f) {
    return new SixTuple<A,B,C,D,E,F>(a, b, c, d, e,f);
  }
} ///:~

public class Ja17_28{
  public static void main(String[] args) {
  }
}



練習29:
// Modify ListPerformance.java so that the Lists hold String objects instead
// of Strings. Use a Generator from the Arrays chapter to create test values.
import java.util.*;
import net.mindview.util.*;
import containers.*;


abstract class Test<C> {
  String name;
  public Test(String name) { this.name = name; }
  // Override this method for different tests.
  // Returns actual number of repetitions of test.
  abstract int test(C container, TestParam tp);
}
class TestParam {
  public final int size;
  public final int loops;
  public TestParam(int size, int loops) {
    this.size = size;
    this.loops = loops;
  }
  // Create an array of TestParam from a varargs sequence:
  public static TestParam[] array(int... values) {
    int size = values.length/2;
    TestParam[] result = new TestParam[size];
    int n = 0;
    for(int i = 0; i < size; i++)
      result[i] = new TestParam(values[n++], values[n++]);
    return result;
  }
  // Convert a String array to a TestParam array:
  public static TestParam[] array(String[] values) {
    int[] vals = new int[values.length];
    for(int i = 0; i < vals.length; i++)
      vals[i] = Integer.decode(values[i]);
    return array(vals);
  }
}
class Tester<C> {
  public static int fieldWidth = 8;
  public static TestParam[] defaultParams= TestParam.array(
    10, 5000, 100, 5000, 1000, 5000, 10000, 500);
  // Override this to modify pre-test initialization:
  protected C initialize(int size) { return container; }
  protected C container;
  private String headline = "";
  private List<Test<C>> tests;
  private static String stringField() {
    return "%" + fieldWidth + "s";
  }
  private static String numberField() {
    return "%" + fieldWidth + "d";
  }
  private static int sizeWidth = 5;
  private static String sizeField = "%" + sizeWidth + "s";
  private TestParam[] paramList = defaultParams;
  public Tester(C container, List<Test<C>> tests) {
    this.container = container;
    this.tests = tests;
    if(container != null)
      headline