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<StringAddress>( Collections.nCopies(4, new StringAddress("Hello")));//建立并填充 System.out.println(list); Collections.fill(list, new StringAddress("World!"));//只能用于替换已有的元素 System.out.println(list); } } /* Output: (Sample) [StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello] [StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!] *///:~
全部的Collection子类型都有一个接受另外一个Collection对象的构造器,用所接受的Collection对象种的元素来填充新的容器。java
import java.util.*; import net.mindview.util.*; class Government implements Generator<String> { String[] foundation = ("strange women lying in ponds " + "distributing swords is no basis for a system of " + "government").split(" "); private int index; public String next() { return foundation[index++]; } } public class CollectionDataTest { public static void main(String[] args) { Set<String> set = new LinkedHashSet<String>( new CollectionData<String>(new Government(), 15)); System.out.println(set); // Using the convenience method: List<String> list=new ArrayList<>(); list.addAll(CollectionData.list(new Government(), 15)); System.out.println(list); } } /* Output: [strange, women, lying, in, ponds, distributing, swords, is, no, basis, for, a, system, of, government] *///:~
Map适配器如今均可以使用各类不一样的Generator、Iterator和常量值的组合来填充Map初始化对象:算法
package net.mindview.util; import java.util.Iterator; import java.util.LinkedHashMap; public class MapData<K, V> extends LinkedHashMap<K, V> { public MapData(Generator<Pair<K, V>> gen, int quantity) { for(int i = 0; i < quantity; ++i) { Pair<K, V> p = (Pair)gen.next(); this.put(p.key, p.value); } } public MapData(Generator<K> genK, Generator<V> genV, int quantity) { for(int i = 0; i < quantity; ++i) { this.put(genK.next(), genV.next()); } } public MapData(Generator<K> genK, V value, int quantity) { for(int i = 0; i < quantity; ++i) { this.put(genK.next(), value); } } public MapData(Iterable<K> genK, Generator<V> genV) { Iterator var4 = genK.iterator(); while(var4.hasNext()) { K key = (Object)var4.next(); this.put(key, genV.next()); } } public MapData(Iterable<K> genK, V value) { Iterator var4 = genK.iterator(); while(var4.hasNext()) { K key = (Object)var4.next(); this.put(key, value); } } public static <K, V> MapData<K, V> map(Generator<Pair<K, V>> gen, int quantity) { return new MapData(gen, quantity); } public static <K, V> MapData<K, V> map(Generator<K> genK, Generator<V> genV, int quantity) { return new MapData(genK, genV, quantity); } public static <K, V> MapData<K, V> map(Generator<K> genK, V value, int quantity) { return new MapData(genK, value, quantity); } public static <K, V> MapData<K, V> map(Iterable<K> genK, Generator<V> genV) { return new MapData(genK, genV); } public static <K, V> MapData<K, V> map(Iterable<K> genK, V value) { return new MapData(genK, value); } }
下面是一个使用MapData的示例。LettersGenerator经过产生一个Iterator还实现了Iterable,经过这种方式,它能够被用来测试MapData.map()方式,而这些方法都须要用到Iterable:编程
import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; class Letters implements Generator<Pair<Integer,String>>, Iterable<Integer> { private int size = 9; private int number = 1; private char letter = 'A'; public Pair<Integer,String> next() { return new Pair<Integer,String>( number++, "" + letter++); } public Iterator<Integer> iterator() { return new Iterator<Integer>() { public Integer next() { return number++; } public boolean hasNext() { return number < size; } public void remove() { throw new UnsupportedOperationException(); } }; } } public class MapDataTest { public static void main(String[] args) { // Pair Generator: print(MapData.map(new Letters(), 11)); // Two separate generators: print(MapData.map(new CountingGenerator.Character(), new RandomGenerator.String(3), 8)); // A key Generator and a single value: print(MapData.map(new CountingGenerator.Character(), "Value", 6)); // An Iterable and a value Generator: print(MapData.map(new Letters(), new RandomGenerator.String(3))); // An Iterable and a single value: print(MapData.map(new Letters(), "Pop")); } } /* Output: {1=A, 2=B, 3=C, 4=D, 5=E, 6=F, 7=G, 8=H, 9=I, 10=J, 11=K} {a=YNz, b=brn, c=yGc, d=FOW, e=ZnT, f=cQr, g=Gse, h=GZM} {a=Value, b=Value, c=Value, d=Value, e=Value, f=Value} {1=mJM, 2=RoE, 3=suE, 4=cUO, 5=neO, 6=EdL, 7=smw, 8=HLG} {1=Pop, 2=Pop, 3=Pop, 4=Pop, 5=Pop, 6=Pop, 7=Pop, 8=Pop} *///:~
下面展现了Collection全部这些方法:api
import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; public class CollectionMethods { public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); c.addAll(Countries.names(6)); c.add("ten"); c.add("eleven"); print(c); // Make an array from the List: Object[] array = c.toArray(); // Make a String array from the List: String[] str = c.toArray(new String[0]);//将list转换为须要的数组 System.out.println("str="+Arrays.deepToString(str)); // Find max and min elements; this means // different things depending on the way // the Comparable interface is implemented: print("Collections.max(c) = " + Collections.max(c)); print("Collections.min(c) = " + Collections.min(c)); // Add a Collection to another Collection Collection<String> c2 = new ArrayList<String>(); c2.addAll(Countries.names(6)); c.addAll(c2); print(c); c.remove(Countries.DATA[0][0]); print(c); c.remove(Countries.DATA[1][0]); print(c); // Remove all components that are // in the argument collection: c.removeAll(c2); print(c); c.addAll(c2); print(c); // Is an element in this Collection? String val = Countries.DATA[3][0]; print("c.contains(" + val + ") = " + c.contains(val)); // Is a Collection in this Collection? print("c.containsAll(c2) = " + c.containsAll(c2)); Collection<String> c3 = ((List<String>)c).subList(3, 5); // Keep all the elements that are in both // c2 and c3 (an intersection of sets): c2.retainAll(c3); print(c2); // Throw away all the elements // in c2 that also appear in c3: c2.removeAll(c3); print("c2.isEmpty() = " + c2.isEmpty()); c = new ArrayList<String>(); c.addAll(Countries.names(6)); print(c); c.clear(); // Remove all elements print("after c.clear():" + c); } }
执行各类不一样的添加和移除的方法在Collection接口中都是可选操做。这意味着实现类并不须要为这些方法提供功能定义。
接口是面向对象设计中的契约,它声明"不管你选择如何实现该接口,我保证你能够向该接口发送这些消息"。可是可选操做违反这个很是基本的原则,它声明调用某些方法将不会执行有意义的行为。
"未获支持的操做"这种方式能够实现Java容器类库一个重要目标:容器应该易学易用。未获支持的操做是一种特例,能够延迟到须要时在实现。
为了让你种方式可以工做:数组
当你用Arrays.aslist()将数组转换未List时,就会获得这样的容器。你能够经过使用Collections类中"不可修改"的方法,选择建立任何会抛出 UnsupportedOperationException的容器。安全
import java.util.*; public class Unsupported { static void test(String msg, List<String> list) { System.out.println("--- " + msg + " ---"); Collection<String> c = list; Collection<String> subList = list.subList(1,8); // Copy of the sublist: Collection<String> c2 = new ArrayList<String>(subList); try { c.retainAll(c2); } catch(Exception e) { System.out.println("retainAll(): " + e); } try { c.removeAll(c2); } catch(Exception e) { System.out.println("removeAll(): " + e); } try { c.clear(); } catch(Exception e) { System.out.println("clear(): " + e); } try { c.add("X"); } catch(Exception e) { System.out.println("add(): " + e); } try { c.addAll(c2); } catch(Exception e) { System.out.println("addAll(): " + e); } try { c.remove("C"); } catch(Exception e) { System.out.println("remove(): " + e); } // The List.set() method modifies the value but // doesn't change the size of the data structure: try { list.set(0, "X"); } catch(Exception e) { System.out.println("List.set(): " + e); } } public static void main(String[] args) { List<String> list = Arrays.asList("A B C D E F G H I J K L".split(" "));//返回固定尺寸的List test("Modifiable Copy", new ArrayList<String>(list)); test("Arrays.asList()", list);// test("unmodifiableList()", Collections.unmodifiableList( new ArrayList<String>(list)));//产生不可修改的列表,任何状况下都不可修改 } } /* Output: --- Modifiable Copy --- --- Arrays.asList() --- retainAll(): java.lang.UnsupportedOperationException removeAll(): java.lang.UnsupportedOperationException clear(): java.lang.UnsupportedOperationException add(): java.lang.UnsupportedOperationException addAll(): java.lang.UnsupportedOperationException remove(): java.lang.UnsupportedOperationException --- unmodifiableList() --- retainAll(): java.lang.UnsupportedOperationException removeAll(): java.lang.UnsupportedOperationException clear(): java.lang.UnsupportedOperationException add(): java.lang.UnsupportedOperationException addAll(): java.lang.UnsupportedOperationException remove(): java.lang.UnsupportedOperationException List.set(): java.lang.UnsupportedOperationException *///:~
Arrays.asList()会生成一个List,它基于一个固定大小的数组,仅支持那些不会改变数组大小的操做。任何会引发对底层数据结构尺寸进行修改的方法都会产生 UnsupportedOperationException异常,以表示对未获取支持操做的调用。数据结构
基本的LIst很容易使用:add()添加对象,get()一次取出一个元素,iterator()获取用于该序列的Iterator。
下面的例子每一个方法都涵盖了不一样动做:并发
import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; public class Lists { private static boolean b; private static String s; private static int i; private static Iterator<String> it; private static ListIterator<String> lit; public static void basicTest(List<String> a) {//包含每一个List均可执行的操做 a.add(1, "x"); // Add at location 1 a.add("x"); // Add at end // Add a collection: a.addAll(Countries.names(25)); // Add a collection starting at location 3: a.addAll(3, Countries.names(25)); b = a.contains("1"); // Is it in there? // Is the entire collection in there? b = a.containsAll(Countries.names(25)); // Lists allow random access, which is cheap // for ArrayList, expensive for LinkedList: s = a.get(1); // Get (typed) object at location 1 i = a.indexOf("1"); // Tell index of object b = a.isEmpty(); // Any elements inside? it = a.iterator(); // Ordinary Iterator lit = a.listIterator(); // ListIterator lit = a.listIterator(3); // Start at loc 3 i = a.lastIndexOf("1"); // Last match a.remove(1); // Remove location 1 a.remove("3"); // Remove this object a.set(1, "y"); // Set location 1 to "y" // Keep everything that's in the argument // (the intersection of the two sets): a.retainAll(Countries.names(25)); // Remove everything that's in the argument: a.removeAll(Countries.names(25)); i = a.size(); // How big is it? a.clear(); // Remove all elements } public static void iterMotion(List<String> a) {//使用Iterator遍历元素 ListIterator<String> it = a.listIterator(); b = it.hasNext(); b = it.hasPrevious(); s = it.next(); i = it.nextIndex(); s = it.previous(); i = it.previousIndex(); } public static void iterManipulation(List<String> a) {//使用Iterator修改元素 ListIterator<String> it = a.listIterator(); it.add("47"); // Must move to an element after add(): it.next(); // Remove the element after the newly produced one: it.remove(); // Must move to an element after remove(): it.next(); // Change the element after the deleted one: it.set("47"); } public static void testVisual(List<String> a) {//查看List的操做效果 print(a); List<String> b = Countries.names(25); print("b = " + b); a.addAll(b); a.addAll(b); print(a); // Insert, remove, and replace elements // using a ListIterator: ListIterator<String> x = a.listIterator(a.size()/2); x.add("one"); print(a); print(x.next()); x.remove(); print(x.next()); x.set("47"); print(a); // Traverse the list backwards: x = a.listIterator(a.size()); while(x.hasPrevious()) printnb(x.previous() + " "); print(); print("testVisual finished"); } // There are some things that only LinkedLists can do: public static void testLinkedList() {//LinkedList专用的操做 LinkedList<String> ll = new LinkedList<String>(); ll.addAll(Countries.names(25)); print(ll); // Treat it like a stack, pushing: ll.addFirst("one"); ll.addFirst("two"); print(ll); // Like "peeking" at the top of a stack: print(ll.getFirst()); // Like popping a stack: print(ll.removeFirst()); print(ll.removeFirst()); // Treat it like a queue, pulling elements // off the tail end: print(ll.removeLast()); print(ll); } public static void main(String[] args) { // Make and fill a new list each time: basicTest( new LinkedList<String>(Countries.names(25))); basicTest( new ArrayList<String>(Countries.names(25))); iterMotion( new LinkedList<String>(Countries.names(25))); iterMotion( new ArrayList<String>(Countries.names(25))); iterManipulation( new LinkedList<String>(Countries.names(25))); iterManipulation( new ArrayList<String>(Countries.names(25))); testVisual( new LinkedList<String>(Countries.names(25))); testLinkedList(); } } /* (Execute to see output) *///:~
类型 | 描述 |
---|---|
Set(interface) | 存入Set的每一个元素都必须是惟一的,由于Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的惟一性。 |
HashSet | 为快速查找而设计的Set。存入HashSet的元素必须定义hashCode() |
TreeSet | 保持次序的Set,底层为树结构。使用它能够从set中提取有序的序列。元素必须实现Comparable接口 |
LinkedHashSet | 具备HashSet的查询速度,且内部使用链表维护元素的顺序。 |
使用特定的Set实现类型而必须定义的方法:app
import java.util.*; 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; } } 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)); } } 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); // Try to add duplicates fill(set, type); System.out.println(set); } public static void main(String[] args) { test(new HashSet<HashType>(), HashType.class);//实现hashCode确保排序而且不会重复 test(new LinkedHashSet<HashType>(), HashType.class); test(new TreeSet<TreeType>(), TreeType.class);//实现Comparable接口排序并不会重复 // Things that don't work: test(new HashSet<SetType>(), SetType.class); test(new HashSet<TreeType>(), TreeType.class); test(new LinkedHashSet<SetType>(), SetType.class); test(new LinkedHashSet<TreeType>(), TreeType.class); try { test(new TreeSet<SetType>(), SetType.class); } catch(Exception e) { System.out.println(e.getMessage()); } try { test(new TreeSet<HashType>(), HashType.class); } catch(Exception e) { System.out.println(e.getMessage()); } } } /* Output: (Sample) [2, 4, 9, 8, 6, 1, 3, 7, 5, 0] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] [9, 9, 7, 5, 1, 2, 6, 3, 0, 7, 2, 4, 4, 7, 9, 1, 3, 6, 2, 4, 3, 0, 5, 0, 8, 8, 8, 6, 5, 1] [0, 5, 5, 6, 5, 0, 3, 1, 9, 8, 4, 2, 3, 9, 7, 3, 4, 4, 0, 7, 1, 9, 6, 2, 1, 8, 2, 8, 6, 7] [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] [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] java.lang.ClassCastException: SetType cannot be cast to java.lang.Comparable java.lang.ClassCastException: HashType cannot be cast to java.lang.Comparable *///:~
SortedSet能够确保元素处于排序状态下:框架
import java.util.*; import static net.mindview.util.Print.*; public class SortedSetDemo { public static void main(String[] args) { SortedSet<String> sortedSet = new TreeSet<String>(); Collections.addAll(sortedSet, "one two three four five six seven eight" .split(" ")); print(sortedSet); String low = sortedSet.first();//返回第一个元素 String high = sortedSet.last();//返回最后一个元素 print(low); print(high); Iterator<String> it = sortedSet.iterator(); for(int i = 0; i <= 6; i++) { if(i == 3) low = it.next(); if(i == 6) high = it.next(); else it.next(); } print(low); print(high); print(sortedSet.subSet(low, high));//生成此Set子集,从low到high print(sortedSet.headSet(high));//生成此Set子集,小于high print(sortedSet.tailSet(low));//生成此Set子集,大于等于low } }
Queue在Java SE5中仅有的两个实现LinkedList和PriorityQueue,它们的差别在于排序行为而不是性能。
Queue实现的大部分操做的基本示例:
import java.util.concurrent.*; import java.util.*; import net.mindview.util.*; 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<String>(), new Gen()); test(new ArrayBlockingQueue<String>(count), new Gen()); test(new ConcurrentLinkedQueue<String>(), new Gen()); test(new LinkedBlockingQueue<String>(), new Gen()); test(new PriorityBlockingQueue<String>(), new Gen()); } } /* Output: 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 *///:~
import java.util.*; 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) { 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("Feed dog", 'A', 2); toDoList.add("Feed bird", 'B', 7); toDoList.add("Mow lawn", 'C', 3); toDoList.add("Water lawn", 'A', 1); toDoList.add("Feed cat", 'B', 1); while(!toDoList.isEmpty())System.out.println(toDoList.remove()); } } /* Output: A1: Water lawn A2: Feed dog B1: Feed cat B7: Feed bird C3: Mow lawn C4: Empty trash *///:~
双向队列就像是一个队列,可是以能够在任何一端添加或移除元素。
实现一个双向队列:
package net.mindview.util; import java.util.LinkedList; public class Deque<T> { private LinkedList<T> deque = new LinkedList(); public Deque() { } public void addFirst(T e) { this.deque.addFirst(e); } public void addLast(T e) { this.deque.addLast(e); } public T getFirst() { return this.deque.getFirst(); } public T getLast() { return this.deque.getLast(); } public T removeFirst() { return this.deque.removeFirst(); } public T removeLast() { return this.deque.removeLast(); } public int size() { return this.deque.size(); } public String toString() { return this.deque.toString(); } }
测试Deque:
import net.mindview.util.*; import static net.mindview.util.Print.*; public class DequeTest { static void fillTest(Deque<Integer> deque) { for(int i = 20; i < 27; i++) deque.addFirst(i); for(int i = 50; i < 55; i++) deque.addLast(i); } public static void main(String[] args) { Deque<Integer> di = new Deque<Integer>(); fillTest(di); print(di); while(di.size() != 0) printnb(di.removeFirst() + " "); print(); fillTest(di); while(di.size() != 0) printnb(di.removeLast() + " "); } } /* Output: [26, 25, 24, 23, 22, 21, 20, 50, 51, 52, 53, 54] 26 25 24 23 22 21 20 50 51 52 53 54 54 53 52 51 50 20 21 22 23 24 25 26 *///:~
映射表(关联数组)的基本思想是它维护的是键-值对关联,所以你可使用建来查找值。标准的Java类库中包含了Map的几种基本实现,包括:HashMap,TreeMap,LinkedHashMap,WeakHashMap,ConcurrentHashMap,IdentityHashMap。它们都有一样的基本接口Map。
下面对Map简单实现:
import static net.mindview.util.Print.*; public class AssociativeArray<K,V> { private Object[][] pairs; private int index; public AssociativeArray(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) { AssociativeArray<String,String> map = new AssociativeArray<String,String>(6); map.put("sky", "blue"); map.put("grass", "green"); map.put("ocean", "dancing"); map.put("tree", "tall"); map.put("earth", "brown"); map.put("sun", "warm"); try { map.put("extra", "object"); // Past the end } catch(ArrayIndexOutOfBoundsException e) { print("Too many objects!"); } print(map); print(map.get("ocean")); } }
HashMap使用了特殊的值,称做散列码,来取代对健的缓慢搜索。
|名称|描述|
|:--:|:--|
|HashMap|Map基于散列表的实现。插入和查询键值对的开销是固定的。能够i经过构造器设置容量和负载因子,以调整容器的性能|
|LinkedHashMap|相似于HashMap,可是迭代遍历它时,取得键值对的顺序是其插入次序,或者是最近最少使用(LRU)次序。只比HashMap慢一点。而在迭代访问时反而更快,由于它使用链表维护内部次序|
|TreeMap|基于红黑树的实现,是惟一带有subMap()的Map|
|WeakHashMap|弱键映射,容许释放映射所指向的对象|
|ConcurrentHashMap|一种线程安全的Map,不涉及同步锁|
|IdentityHashMap|使用==代替equals()对键进行比较的散列映射|
散列是映射中存储元素时最经常使用的方式。
对Map中使用的键要求与对Set中的元素要求一致。
展现经过Map接口可用的操做:
import java.util.concurrent.*; import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; public class Maps { 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 TreeMap<Integer,String>()); test(new LinkedHashMap<Integer,String>()); test(new IdentityHashMap<Integer,String>()); test(new ConcurrentHashMap<Integer,String>()); test(new WeakHashMap<Integer,String>()); } }
使用SortedMap(TreeMap是其现阶段惟一实现),能够确保键处于排序状态。
import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; public class SortedMapDemo { public static void main(String[] args) { TreeMap<Integer,String> sortedMap = new TreeMap<Integer,String>(new CountingMapData(10)); print(sortedMap); Integer low = sortedMap.firstKey(); Integer high = sortedMap.lastKey(); print(low); print(high); Iterator<Integer> it = sortedMap.keySet().iterator(); for(int i = 0; i <= 6; i++) { if(i == 3) low = it.next(); if(i == 6) high = it.next(); else it.next(); } print(low); print(high); print(sortedMap.subMap(low, high)); print(sortedMap.headMap(high)); print(sortedMap.tailMap(low)); } }
为了提升速度,LinkedHashMap散列化全部的元素,可是在遍历键值对时,却又以元素的插入顺序返回键值对。
能够在构造器中设定LinkedHashMap,使之采用基于访问最近最少使用算法,因而没有被访问过的元素就会出如今队列的前面。
import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; public class LinkedHashMapDemo { public static void main(String[] args) { LinkedHashMap<Integer,String> linkedMap = new LinkedHashMap<Integer,String>( new CountingMapData(9)); print(linkedMap); // Least-recently-used order: linkedMap = new LinkedHashMap<Integer,String>(16, 0.75f, true); linkedMap.putAll(new CountingMapData(9)); print(linkedMap); for(int i = 0; i < 6; i++) // Cause accesses: linkedMap.get(i); print(linkedMap); linkedMap.get(0); print(linkedMap); } } /* Output: {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} *///:~
当本身建立用做HashMap的键的类,有可能会忘记在其中放置必须的方法,而这是一般会犯的一个错误:
public class Groundhog { protected int number; public Groundhog(int n) { number = n; } public String toString() { return "Groundhog #" + number; } }
import java.util.*; public class Prediction { private static Random rand = new Random(47); private boolean shadow = rand.nextDouble() > 0.5; public String toString() { if(shadow) return "Six more weeks of Winter!"; else return "Early Spring!"; } } ///:~
import java.lang.reflect.*; import java.util.*; import static net.mindview.util.Print.*; public class SpringDetector { // Uses a Groundhog or class derived from Groundhog: public static <T extends Groundhog> void detectSpring(Class<T> type) throws Exception { Constructor<T> ghog = type.getConstructor(int.class);// 经过类对象的getConstructor()或getDeclaredConstructor()方法得到构造器(Constructor)对象并调用其newInstance()方法建立对象 Map<Groundhog,Prediction> map = new HashMap<Groundhog,Prediction>(); for(int i = 0; i < 10; i++) map.put(ghog.newInstance(i), new Prediction()); print("map = " + map); Groundhog gh = ghog.newInstance(3); print("Looking up prediction for " + gh); if(map.containsKey(gh)) print(map.get(gh)); else print("Key not found: " + gh); } public static void main(String[] args) throws Exception { detectSpring(Groundhog.class); } } /* Output: map = {Groundhog #3=Early Spring!, Groundhog #7=Early Spring!, Groundhog #5=Early Spring!, Groundhog #9=Six more weeks of Winter!, Groundhog #8=Six more weeks of Winter!, Groundhog #0=Six more weeks of Winter!, Groundhog #6=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #1=Six more weeks of Winter!, Groundhog #2=Early Spring!} Looking up prediction for Groundhog #3 Key not found: Groundhog #3 *///:~
从上面结果能够看到,在map中没法找到3这个key,那是由于这里Groundhog 没有实现hashCode()和equals()这两个方法,默认使用Object中的这两个方法进行比较,Object中的hashCode()是根据对象地址计算出来的。
重载实现两个方法便可:
public class Groundhog { protected int number; public Groundhog(int n) { number = n; } public String toString() { return "Groundhog #" + number; } @Override public int hashCode() { return number; } @Override public boolean equals(Object obj) { return obj instanceof Groundhog && (number==((Groundhog)obj).number); } ///:~
ashMap使用equals()判断当前的键是否与表中存在的键相同。
正确的equals()方法必须知足下面5个条件:
对任何不是null的x,x.equals(null)必定返回false。
import java.util.*; import net.mindview.util.*; public 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, 这里使用Object是由于泛型引入太晚致使 if(!keys.contains(key)) return null; return values.get(keys.indexOf(key)); } 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(15)); System.out.println(m); System.out.println(m.get("BULGARIA")); System.out.println(m.entrySet()); } }
Map.Entry是一个接口,用来描述依赖于实现的结构,若是要建立本身的Map类型,就必须同时定义Map.Entry的实现:
import java.util.*; 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; } 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; } }
散列的价值在于速度:散列使得查询得以快速进行。
散列跟进一步,它将键保持在某处,以便可以很快找到。存储一组元素最快的数据结构就是数组,因此使用它来表示键的信息。
数组不能调整容量,但咱们不但愿键的数量被数组的容量限制。
解决办法:数组并不保存键自己。而是经过键对象生成一个数字,将其做为数组的下标,这个数字就是散列码。
为解决数组容量被固定的问题,不一样的键能够产生相同的下标。也就是说,可能会有冲突。所以,数组多大就不重要了,任何键总能在数组中找到位置。
因而查询一个值得过程首先就是计算散列码,而后使用散列码查询数组。若是可以保证没有冲突,那就是一个完美散列函数了。可是这种状况只是特例。冲突有外部连接处理:数组并不直接保存值,而是保存值得list。而后对list中的值使用equals()方法进行线性查询。
public 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)) { 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()); } }
设计hashCode()最重要的因素就是:不管什么时候,对同一个对象调用hashCode()都应该产生一样的值。
下面String为例。String有个特色:若是程序中有多个String对象,都包含相同的字符串序列,那么这些String对象都映射到同一块内存区域:
public class StringHashCode { public static void main(String[] args) { String[] hellos = "Hello Hello".split(" "); System.out.println(hellos[0].hashCode()); System.out.println(hellos[1].hashCode()); } } /* Output: (Sample) 69609650 69609650 *///:~
对于String而言,hashCode()明显是基于String的内容的。
hashCode()的散列码必须是基于内容生成,没必要独一无二,可是经过hashCOde()和equals()必须可以彻底肯定对象。
怎样写出一份像样的hashCode基本指导:
import java.util.*; import static net.mindview.util.Print.*; public 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(); result = 37 * result + id; return result; } 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,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)); } } } /* Output: (Sample) {String: hi id: 4 hashCode(): 146450=3, String: hi id: 1 hashCode(): 146447=0, String: hi id: 3 hashCode(): 146449=2, String: hi id: 5 hashCode(): 146451=4, String: hi id: 2 hashCode(): 146448=1} 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 Looking up String: hi id: 4 hashCode(): 146450 3 Looking up String: hi id: 5 hashCode(): 146451 4 *///:~
第二个实例Individual:
package pets; 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; } // 'name' is optional: public Individual() {} public String toString() { return getClass().getSimpleName() + (name == null ? "" : " " + name); } public long id() { return 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) { // Compare by class name first: 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) { int secondCompare = name.compareTo(arg.name); if(secondCompare != 0) return secondCompare; } return (arg.id < id ? -1 : (arg.id == id ? 0 : 1)); } } ///:~
compareTO()方法有一个比较结构,所以它会产生一个排序序列,排序规则首先按照实际类型排序,而后若是有名字按名字排序,最后按建立顺序排序:
import pets.*; import java.util.*; public class IndividualTest { public static void main(String[] args) { Set<Individual> pets = new TreeSet<Individual>(); for(List<? extends Pet> lp : MapOfList.petPeople.values()) for(Pet p : lp) pets.add(p); System.out.println(pets); } } /* Output: [Cat Elsie May, Cat Pinkola, Cat Shackleton, Cat Stanford aka Stinky el Negro, Cymric Molly, Dog Margrett, Mutt Spot, Pug Louie aka Louis Snorkelstein Dupree, Rat Fizzy, Rat Freckly, Rat Fuzzy] *///:~
尽管实际上只有4种容器:Map,List,Set,Queue,可是每种接口都有不止一个实现版本。
容器之间的区别一般归结为由什么在背后支持它们。也就是说,所使用的接口是由什么样的数据结构实现的。
ArrayList底层由数组支持,LinkedList由双向链表实现。
Set被实现为TreeSet(基于TreeMap,生成一个老是处于排序状态的Set),HashSet(查询速度最快)或LinkedHashSet(保持元素插入顺序)。
下面的代码构建了一个基类,从中能够建立一个匿名内部类列表,其中每一个匿名内部类都用于各类不一样的测试,它们每一个都被看成测试过程的一部分而被调用。
待测容器类型是泛型参数C。每一个对象都存储了该测试的名字。
public 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); } ///:~
第二个版本,接受相同类型的列表,可是它的值都在String中——经过这种方式,它能够用来解析命令行参数:
public 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); } } ///:~
import java.util.*; public 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 = container.getClass().getSimpleName(); } public Tester(C container, List<Test<C>> tests, TestParam[] paramList) { this(container, tests); this.paramList = paramList; } public void setHeadline(String newHeadline) { headline = newHeadline; } // Generic methods for convenience : public static <C> void run(C cntnr, List<Test<C>> tests){ new Tester<C>(cntnr, tests).timedTest(); } public static <C> void run(C cntnr, List<Test<C>> tests, TestParam[] paramList) { new Tester<C>(cntnr, tests, paramList).timedTest(); } private void displayHeader() {//头信息 // Calculate width and pad with '-': int width = fieldWidth * tests.size() + sizeWidth; int dashLength = width - headline.length() - 1; StringBuilder head = new StringBuilder(width); for(int i = 0; i < dashLength/2; i++) head.append('-'); head.append(' '); head.append(headline); head.append(' '); for(int i = 0; i < dashLength/2; i++) head.append('-'); System.out.println(head); // Print column headers: System.out.format(sizeField, "size"); for(Test test : tests) System.out.format(stringField(), test.name); System.out.println(); } // Run the tests for this container: public void timedTest() { displayHeader(); for(TestParam param : paramList) { System.out.format(sizeField, param.size); for(Test<C> test : tests) { C kontainer = initialize(param.size); long start = System.nanoTime();//计算操做所需纳秒数 // Call the overriden method: int reps = test.test(kontainer, param); long duration = System.nanoTime() - start; long timePerRep = duration / reps; // Nanoseconds System.out.format(numberField(), timePerRep); } System.out.println(); } } } ///:~
List操做中最本质部分的性能测试,它还展现了Queue中最重要的操做。在测试中,Queue操做只应用到LinkedList之上:
//: containers/ListPerformance.java // Demonstrates performance differences in Lists. // {Args: 100 500} Small to keep build testing short import java.util.*; import net.mindview.util.*; public class ListPerformance { static Random rand = new Random(); static int reps = 1000; static List<Test<List<Integer>>> tests = new ArrayList<Test<List<Integer>>>(); static List<Test<LinkedList<Integer>>> qTests = new ArrayList<Test<LinkedList<Integer>>>(); static { tests.add(new Test<List<Integer>>("add") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; int listSize = tp.size; for (int i = 0; i < loops; i++) { list.clear(); for (int j = 0; j < listSize; j++) list.add(j); } return loops * listSize; } }); tests.add(new Test<List<Integer>>("get") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for (int i = 0; i < loops; i++) list.get(rand.nextInt(listSize)); return loops; } }); tests.add(new Test<List<Integer>>("set") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops * reps; int listSize = list.size(); for (int i = 0; i < loops; i++) list.set(rand.nextInt(listSize), 47); return loops; } }); tests.add(new Test<List<Integer>>("iteradd") { int test(List<Integer> list, TestParam tp) { final int LOOPS = 1000000; int half = list.size() / 2; ListIterator<Integer> it = list.listIterator(half); for (int i = 0; i < LOOPS; i++) it.add(47); return LOOPS; } }); tests.add(new Test<List<Integer>>("insert") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; for (int i = 0; i < loops; i++) list.add(5, 47); // Minimize random-access cost return loops; } }); tests.add(new Test<List<Integer>>("remove") { int test(List<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); list.addAll(new CountingIntegerList(size)); while (list.size() > 5) list.remove(5); // Minimize random-access cost } return loops * size; } }); // Tests for queue behavior: qTests.add(new Test<LinkedList<Integer>>("addFirst") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); for (int j = 0; j < size; j++) list.addFirst(47); } return loops * size; } }); qTests.add(new Test<LinkedList<Integer>>("addLast") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); for (int j = 0; j < size; j++) list.addLast(47); } return loops * size; } }); qTests.add( new Test<LinkedList<Integer>>("rmFirst") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); list.addAll(new CountingIntegerList(size)); while (list.size() > 0) list.removeFirst(); } return loops * size; } }); qTests.add(new Test<LinkedList<Integer>>("rmLast") { int test(LinkedList<Integer> list, TestParam tp) { int loops = tp.loops; int size = tp.size; for (int i = 0; i < loops; i++) { list.clear(); list.addAll(new CountingIntegerList(size)); while (list.size() > 0) list.removeLast(); } return loops * size; } }); } static class ListTester extends Tester<List<Integer>> { public ListTester(List<Integer> container, List<Test<List<Integer>>> tests) { super(container, tests); } // Fill to the appropriate size before each test: @Override protected List<Integer> initialize(int size) { container.clear(); container.addAll(new CountingIntegerList(size)); return container; } // Convenience method: public static void run(List<Integer> list, List<Test<List<Integer>>> tests) { new ListTester(list, tests).timedTest(); } } public static void main(String[] args) { if (args.length > 0) Tester.defaultParams = TestParam.array(args); // Can only do these two tests on an array: Tester<List<Integer>> arrayTest = new Tester<List<Integer>>(null, tests.subList(1, 3)) { // This will be called before each test. It // produces a non-resizeable array-backed list: @Override protected List<Integer> initialize(int size) { Integer[] ia = Generated.array(Integer.class, new CountingGenerator.Integer(), size); return Arrays.asList(ia); } }; arrayTest.setHeadline("Array as List"); arrayTest.timedTest(); Tester.defaultParams = TestParam.array( 10, 5000, 100, 5000, 1000, 1000, 10000, 200); if (args.length > 0) Tester.defaultParams = TestParam.array(args); ListTester.run(new ArrayList<Integer>(), tests); ListTester.run(new LinkedList<Integer>(), tests); ListTester.run(new Vector<Integer>(), tests); Tester.fieldWidth = 12; Tester<LinkedList<Integer>> qTest = new Tester<LinkedList<Integer>>( new LinkedList<Integer>(), qTests); qTest.setHeadline("Queue tests"); qTest.timedTest(); } } /* Output: (Sample) --- Array as List --- size get set 10 130 183 100 130 164 1000 129 165 10000 129 165 --------------------- ArrayList --------------------- size add get set iteradd insert remove 10 121 139 191 435 3952 446 100 72 141 191 247 3934 296 1000 98 141 194 839 2202 923 10000 122 144 190 6880 14042 7333 --------------------- LinkedList --------------------- size add get set iteradd insert remove 10 182 164 198 658 366 262 100 106 202 230 457 108 201 1000 133 1289 1353 430 136 239 10000 172 13648 13187 435 255 239 ----------------------- Vector ----------------------- size add get set iteradd insert remove 10 129 145 187 290 3635 253 100 72 144 190 263 3691 292 1000 99 145 193 846 2162 927 10000 108 145 186 6871 14730 7135 -------------------- Queue tests -------------------- size addFirst addLast rmFirst rmLast 10 199 163 251 253 100 98 92 180 179 1000 99 93 216 212 10000 111 109 262 384 *///:~
get和set测试都使用了随机数生成器来执行对List的随机访问。对于背后是数组支撑的List和ArrayList,不管列表大小如何,这些访问都快速一致。而对于LinkedList访问时间对于较大的列代表显增长,若是须要执行大量随机访问,连接链表不会是一种好的选择。
使用迭代器在列表中间插入新的元素。对于ArrayList,当列表变大时,其开销将变得高昂,可是对于LinkedList,相对来讲比较低廉,而且不随列表尺寸而发生变化。这是由于ArrayList在插入时,必须建立空间并将它的全部引用向前移动,这会随ArrayList的尺寸增长而产生高昂的代价。LinkedList自须要连接新的元素,而没必要修改列表中剩余的元素,一次你能够认为列表尺寸若是变化,其代价大体相同。
没有选择List两端的元素时由于LinkedList对List端点会进行特殊处理——这使得在将LinkedList用做Queue时,速度能够获得提升。
在LinkedList中的插入和移除代价至关低廉,而且不随列表尺寸发生变化,可是对于ArrayList,插入操做代价很高昂,而且不随列表尺寸的增长而增长。
从Queue测试中,能够看到LinkedList能够多么快速的从列表的端点插入和移除元素,这正是对Queue行为所作的优化。
将ArrayList做为默认首选,只有你须要使用额外的功能,或者当程序的性能由于常常从表中间进行插入和删除而变差的时候,才去选择LinkedList。
CopyOnWriteArrayList是List的一个特殊实现,专门用于并发编程。
import java.util.*; public class SetPerformance { static List<Test<Set<Integer>>> tests = new ArrayList<Test<Set<Integer>>>(); static { tests.add(new Test<Set<Integer>>("add") { int test(Set<Integer> set, TestParam tp) { int loops = tp.loops; int size = tp.size; for(int i = 0; i < loops; i++) { set.clear(); for(int j = 0; j < size; j++) set.add(j); } return loops * size; } }); tests.add(new Test<Set<Integer>>("contains") { int test(Set<Integer> set, TestParam tp) { int loops = tp.loops; int span = tp.size * 2; for(int i = 0; i < loops; i++) for(int j = 0; j < span; j++) set.contains(j); return loops * span; } }); tests.add(new Test<Set<Integer>>("iterate") { int test(Set<Integer> set, TestParam tp) { int loops = tp.loops * 10; for(int i = 0; i < loops; i++) { Iterator<Integer> it = set.iterator(); while(it.hasNext()) it.next(); } return loops * set.size(); } }); } public static void main(String[] args) { if(args.length > 0) Tester.defaultParams = TestParam.array(args); Tester.fieldWidth = 10; Tester.run(new TreeSet<Integer>(), tests); Tester.run(new HashSet<Integer>(), tests); Tester.run(new LinkedHashSet<Integer>(), tests); } } /* Output: (Sample) ------------- TreeSet ------------- size add contains iterate 10 746 173 89 100 501 264 68 1000 714 410 69 10000 1975 552 69 ------------- HashSet ------------- size add contains iterate 10 308 91 94 100 178 75 73 1000 216 110 72 10000 711 215 100 ---------- LinkedHashSet ---------- size add contains iterate 10 350 65 83 100 270 74 55 1000 303 111 54 10000 1615 256 58 *///:~
HashSet的性能基本上老是比TreeSet的好,特别是在添加和查询元素时,而这两个操做是最重要的操做。TreeSet存在的惟一缘由是它能够维持元素的排序状态。
对于插入操做,LinkedHashSet比HashSet的代价更高,这是由维护链表所带来额外开销形成的。
//: containers/MapPerformance.java // Demonstrates performance differences in Maps. // {Args: 100 5000} Small to keep build testing short import java.util.*; public class MapPerformance { static List<Test<Map<Integer,Integer>>> tests = new ArrayList<Test<Map<Integer,Integer>>>(); static { tests.add(new Test<Map<Integer,Integer>>("put") { int test(Map<Integer,Integer> map, TestParam tp) { int loops = tp.loops; int size = tp.size; for(int i = 0; i < loops; i++) { map.clear(); for(int j = 0; j < size; j++) map.put(j, j); } return loops * size; } }); tests.add(new Test<Map<Integer,Integer>>("get") { int test(Map<Integer,Integer> map, TestParam tp) { int loops = tp.loops; int span = tp.size * 2; for(int i = 0; i < loops; i++) for(int j = 0; j < span; j++) map.get(j); return loops * span; } }); tests.add(new Test<Map<Integer,Integer>>("iterate") { int test(Map<Integer,Integer> map, TestParam tp) { int loops = tp.loops * 10; for(int i = 0; i < loops; i ++) { Iterator it = map.entrySet().iterator(); while(it.hasNext()) it.next(); } return loops * map.size(); } }); } public static void main(String[] args) { if(args.length > 0) Tester.defaultParams = TestParam.array(args); Tester.run(new TreeMap<Integer,Integer>(), tests); Tester.run(new HashMap<Integer,Integer>(), tests); Tester.run(new LinkedHashMap<Integer,Integer>(),tests); Tester.run( new IdentityHashMap<Integer,Integer>(), tests); Tester.run(new WeakHashMap<Integer,Integer>(), tests); Tester.run(new Hashtable<Integer,Integer>(), tests); } } /* Output: (Sample) ---------- TreeMap ---------- size put get iterate 10 748 168 100 100 506 264 76 1000 771 450 78 10000 2962 561 83 ---------- HashMap ---------- size put get iterate 10 281 76 93 100 179 70 73 1000 267 102 72 10000 1305 265 97 ------- LinkedHashMap ------- size put get iterate 10 354 100 72 100 273 89 50 1000 385 222 56 10000 2787 341 56 ------ IdentityHashMap ------ size put get iterate 10 290 144 101 100 204 287 132 1000 508 336 77 10000 767 266 56 -------- WeakHashMap -------- size put get iterate 10 484 146 151 100 292 126 117 1000 411 136 152 10000 2165 138 555 --------- Hashtable --------- size put get iterate 10 264 113 113 100 181 105 76 1000 260 201 80 10000 1245 134 77 *///:~
全部的Map实现的插入操做都会随着Map尺寸的变大明显变慢。可是查找的代价比插入小不少。
Hashtable的性能大致和HashMap至关。由于HashMap是用来替代Hashtable的,所以它们使用了相同的底层存储和查找机制。
TreeMap是一种建立有序列表的方式。树的行为是:老是保证有序,而且没必要进行特殊的排序。一旦你填充了一个TreeMap,就能够调用keySet()方法来获取键的Set视图,而后调用toArray()来产生这些键构成的数组。
可使用静态方法Arrays.binarySearch()在排序数组中快速查找对象。
LingkedHashMap在插入时比HashMap慢一点,由于它维护散列数据结构的同时还要维护链表。就是由于这个列表,使得其迭代速度更快。
IdentityHashMap则具备彻底不一样的性能,由于它使用==而不是equals()来比较元素。
能够经过手工调整HashMap来提升性能,从而知足咱们特定应用的需求:
容量:表中的桶位数。
初始容量:表在建立时所拥有的桶位数。
尺寸:表中当前存储的项数。
负载因子:尺寸/容量。负载轻的表产生的可能性小,所以对于插入和查找都是最理想的。
HashMap使用的默认负载因子时0.75,这个因子在时间和空间代价之间达到了平衡。
Java中有大量用于容器得卓越的使用方法,它们表示为java.util.Collection类nebulous的静态方法。
import java.util.*; import static net.mindview.util.Print.*; public class Utilities { static List<String> list = Arrays.asList( "one Two three Four five six one".split(" ")); public static void main(String[] args) { print(list); print("'list' disjoint (Four)?: " + Collections.disjoint(list, Collections.singletonList("Four")));//disjoint 两个集合中没有相同的元素返回true print("max: " + Collections.max(list)); print("min: " + Collections.min(list));//max,min返回最小或最大的元素,用Comparator比较 print("max w/ comparator: " + Collections.max(list, String.CASE_INSENSITIVE_ORDER)); print("min w/ comparator: " + Collections.min(list, String.CASE_INSENSITIVE_ORDER)); List<String> sublist = Arrays.asList("Four five six".split(" ")); print("indexOfSubList: " + Collections.indexOfSubList(list, sublist));//返回sublist再list中第一个出现的位置,找不到返回-1 print("lastIndexOfSubList: " + Collections.lastIndexOfSubList(list, sublist));//返回sublist再list中最后一次出现的位置,找不到返回-1 Collections.replaceAll(list, "one", "Yo");//使用newVal替换全部的oldVal print("replaceAll: " + list); Collections.reverse(list);//逆转全部元素次序 print("reverse: " + list); Collections.rotate(list, 3);//全部元素向后移动distance个位置,将最后元素循环到前面来 print("rotate: " + list); List<String> source = Arrays.asList("in the matrix".split(" ")); Collections.copy(list, source); print("copy: " + list); Collections.swap(list, 0, list.size() - 1);//交换list中位置i与位置j的元素。 print("swap: " + list); Collections.shuffle(list, new Random(47));//随机改变指定列表顺序 print("shuffled: " + list); Collections.fill(list, "pop"); print("fill: " + list); print("frequency of 'pop': " + Collections.frequency(list, "pop")); List<String> dups = Collections.nCopies(3, "snap");//返回大小为n的List<T>,次List不可改变 print("dups: " + dups); print("'list' disjoint 'dups'?: " + Collections.disjoint(list, dups));//当没有相同的元素时,返回true // Getting an old-style Enumeration: Enumeration<String> e = Collections.enumeration(dups);//生成一个旧式的Enumeraton<T> Vector<String> v = new Vector<String>(); while(e.hasMoreElements()) v.addElement(e.nextElement()); // Converting an old-style Vector // to a List via an Enumeration: ArrayList<String> arrayList = Collections.list(v.elements()); print("arrayList: " + arrayList); } } /* Output: [one, Two, three, Four, five, six, one] 'list' disjoint (Four)?: false max: three min: Four max w/ comparator: Two min w/ comparator: five indexOfSubList: 3 lastIndexOfSubList: 3 replaceAll: [Yo, Two, three, Four, five, six, Yo] reverse: [Yo, six, five, Four, three, Two, Yo] rotate: [three, Two, Yo, Yo, six, five, Four] copy: [in, the, matrix, Yo, six, five, Four] swap: [Four, the, matrix, Yo, six, five, in] shuffled: [six, matrix, the, Four, Yo, five, in] fill: [pop, pop, pop, pop, pop, pop, pop] frequency of 'pop': 7 dups: [snap, snap, snap] 'list' disjoint 'dups'?: true arrayList: [snap, snap, snap] *///:~
List排序和查询所使用的方法与对象数组所使用的相应方法有相同的名字与语法,只是用Collections的static方法代替Arrays的方法而已。
import java.util.*; import static net.mindview.util.Print.*; public class ListSortSearch { public static void main(String[] args) { List<String> list = new ArrayList<String>(Utilities.list); list.addAll(Utilities.list); print(list); Collections.shuffle(list, new Random(47)); print("Shuffled: " + list); // Use a ListIterator to trim off the last elements: ListIterator<String> it = list.listIterator(10); while(it.hasNext()) { it.next(); it.remove(); } print("Trimmed: " + list); Collections.sort(list); print("Sorted: " + list); String key = list.get(7); int index = Collections.binarySearch(list, key); print("Location of " + key + " is " + index + ", list.get(" + index + ") = " + list.get(index)); Collections.sort(list, String.CASE_INSENSITIVE_ORDER); print("Case-insensitive sorted: " + list); key = list.get(7); index = Collections.binarySearch(list, key, String.CASE_INSENSITIVE_ORDER); print("Location of " + key + " is " + index + ", list.get(" + index + ") = " + list.get(index)); } }
生成各类只读容器:
import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; public class ReadOnly { static Collection<String> data = new ArrayList<String>(Countries.names(6)); public static void main(String[] args) { Collection<String> c = Collections.unmodifiableCollection( new ArrayList<String>(data)); print(c); // Reading is OK //! c.add("one"); // Can't change it List<String> a = Collections.unmodifiableList( new ArrayList<String>(data)); ListIterator<String> lit = a.listIterator(); print(lit.next()); // Reading is OK //! lit.add("one"); // Can't change it Set<String> s = Collections.unmodifiableSet( new HashSet<String>(data)); print(s); // Reading is OK //! s.add("one"); // Can't change it // For a SortedSet: Set<String> ss = Collections.unmodifiableSortedSet( new TreeSet<String>(data)); Map<String,String> m = Collections.unmodifiableMap( new HashMap<String,String>(Countries.capitals(6))); print(m); // Reading is OK //! m.put("Ralph", "Howdy!"); // For a SortedMap: Map<String,String> sm = Collections.unmodifiableSortedMap( new TreeMap<String,String>(Countries.capitals(6))); } }
对特定类型的"不可修改的"方法的调用并不会产生编译时的检查,可是转换完成后,任何改变容器内容的操做都会引起UnsupportedOperationException异常。
import java.util.*; public class Synchronization { public static void main(String[] args) { Collection<String> c = Collections.synchronizedCollection( new ArrayList<String>()); List<String> list = Collections.synchronizedList( new ArrayList<String>()); Set<String> s = Collections.synchronizedSet( new HashSet<String>()); Set<String> ss = Collections.synchronizedSortedSet( new TreeSet<String>()); Map<String,String> m = Collections.synchronizedMap( new HashMap<String,String>()); Map<String,String> sm = Collections.synchronizedSortedMap( new TreeMap<String,String>()); } } ///:~
直接将新生成的容器传递给适当的同步方法。
Java容器有一种保护机制,可以防止多个进程同时修改同一个容器的内容。
Java容器类类库采用快速报错机制,它会探查容器上的任何除了你进程所进行的操做之外的全部变化,一旦发现其余进程修改了容器,就会抛出ConcurrentModificationException异常。
import java.util.*; public class FailFast { public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); Iterator<String> it = c.iterator(); c.add("An object"); try { String s = it.next(); } catch(ConcurrentModificationException e) { System.out.println(e); } } } /* Output: java.util.ConcurrentModificationException *///:~