ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/MapCheck.java
Revision: 1.4
Committed: Wed Jul 1 12:11:44 2009 UTC (14 years, 10 months ago) by dl
Branch: MAIN
Changes since 1.3: +477 -364 lines
Log Message:
Overhaul

File Contents

# User Rev Content
1 dl 1.2 /*
2     * Written by Doug Lea with assistance from members of JCP JSR-166
3     * Expert Group and released to the public domain, as explained at
4     * http://creativecommons.org/licenses/publicdomain
5     */
6 dl 1.1 /**
7     * @test
8     * @synopsis Times and checks basic map operations
9 dl 1.4 *
10     * When run with "s" second arg, this requires file "testwords", which
11     * is best used with real words. We can't check in this file, but you
12     * can create one from a real dictonary (1 line per word) and then run
13     * linux "shuf" to randomize entries.
14 dl 1.1 */
15     import java.util.*;
16     import java.io.*;
17    
18     public class MapCheck {
19 dl 1.4 static final Object MISSING = new Object();
20     static TestTimer timer = new TestTimer();
21     static Class eclass;
22     static Class mapClass = java.util.concurrent.ConcurrentHashMap.class;
23 dl 1.1
24 dl 1.4 static final LoopHelpers.SimpleRandom srng = new LoopHelpers.SimpleRandom();
25     static final Random rng = new Random(3152688);
26 dl 1.1
27 dl 1.4 static volatile int checkSum;
28 dl 1.1
29     static void reallyAssert(boolean b) {
30     if (!b) throw new Error("Failed Assertion");
31     }
32    
33     public static void main(String[] args) throws Exception {
34 dl 1.4 int numTests = 100;
35     int size = 36864; // about midway of HashMap resize interval
36 dl 1.1
37 dl 1.4 if (args.length == 0)
38     System.out.println("Usage: MapCheck mapclass [int|float|string|object] [trials] [size] [serialtest]");
39    
40 dl 1.1 if (args.length > 0) {
41     try {
42     mapClass = Class.forName(args[0]);
43     } catch(ClassNotFoundException e) {
44     throw new RuntimeException("Class " + args[0] + " not found.");
45     }
46 dl 1.4 }
47    
48     if (args.length > 1) {
49     String et = args[1].toLowerCase();
50     if (et.startsWith("i"))
51     eclass = java.lang.Integer.class;
52     else if (et.startsWith("f"))
53     eclass = java.lang.Float.class;
54     else if (et.startsWith("s"))
55     eclass = java.lang.String.class;
56 dl 1.1 }
57 dl 1.4 if (eclass == null)
58     eclass = Object.class;
59 dl 1.1
60 dl 1.4 if (args.length > 2)
61     numTests = Integer.parseInt(args[2]);
62 dl 1.1
63 dl 1.4 if (args.length > 3)
64     size = Integer.parseInt(args[3]);
65 dl 1.1
66 dl 1.4 boolean doSerializeTest = args.length > 4;
67 dl 1.1
68 dl 1.4 while ((size & 3) != 0) ++size;
69 dl 1.1
70 dl 1.4 System.out.print("Class: " + mapClass.getName());
71     System.out.print(" elements: " + eclass.getName());
72     System.out.print(" trials: " + numTests);
73     System.out.print(" size: " + size);
74     System.out.println();
75 dl 1.1
76     Object[] key = new Object[size];
77 dl 1.4 Object[] absent = new Object[size];
78     initializeKeys(key, absent, size);
79 dl 1.1
80 dl 1.4 precheck(size, key, absent);
81 dl 1.1
82     for (int rep = 0; rep < numTests; ++rep) {
83 dl 1.4 mainTest(newMap(), key, absent);
84     if ((rep & 3) == 3 && rep < numTests - 1) {
85     shuffle(key);
86     // Thread.sleep(10);
87     }
88 dl 1.1 }
89    
90     TestTimer.printStats();
91    
92 dl 1.4 checkNullKey();
93 dl 1.1
94     if (doSerializeTest)
95 dl 1.4 serTest(newMap(), size);
96 dl 1.1 }
97    
98 dl 1.4 static Map newMap() {
99 dl 1.1 try {
100 dl 1.4 return (Map)mapClass.newInstance();
101 dl 1.1 } catch(Exception e) {
102 dl 1.4 throw new RuntimeException("Can't instantiate " + mapClass + ": " + e);
103 dl 1.1 }
104     }
105    
106 dl 1.4 static void precheck(int n, Object[] key, Object[] abs) {
107     int ck = 0;
108     Map s = newMap();
109     for (int i = 0; i < n; i++) {
110     Object k = key[i];
111     if (k == null) throw new Error("Null key at" + i);
112     ck += System.identityHashCode(k);
113     Object v = s.put(k, k);
114     if (v != null)
115     throw new Error("Duplicate " + k + " / " + v);
116     }
117     for (int i = 0; i < n; i++) {
118     Object k = abs[i];
119     if (k == null) throw new Error("Null key at" + i);
120     ck += System.identityHashCode(k);
121     Object v = s.put(k, k);
122     if (v != null)
123     throw new Error("Duplicate " + k + " / " + v);
124     }
125     checkSum += ck;
126     }
127 dl 1.1
128 dl 1.4 static void checkNullKey() {
129     Map m = newMap();
130     Object x = new Object();
131     Object v;
132     try {
133     m.put(null, x);
134     v = m.get(null);
135     } catch(NullPointerException npe) {
136     System.out.println("Map does not allow null keys");
137     return;
138     }
139     if (v != x) throw new Error();
140     if (m.remove(null) != v) throw new Error();
141     if (m.get(null) != null) throw new Error();
142 dl 1.1 }
143    
144 dl 1.4
145     static void getTest(String nm, int n, Map s, Object[] key, int expect) {
146 dl 1.1 int sum = 0;
147 dl 1.4 timer.start(nm, n);
148     for (int i = 0; i < n; i++) {
149     Object v = s.get(key[i]);
150     if (v != null && v.getClass() == eclass)
151     ++sum;
152     }
153     timer.finish();
154     reallyAssert (sum == expect);
155     checkSum += sum;
156 dl 1.1 }
157    
158    
159 dl 1.4 // unused
160     static void getTestBoxed(String nm, int n, Map s, Object[] key, int expect) {
161 dl 1.1 int sum = 0;
162 dl 1.4 Map<Integer,Integer> intMap = (Map<Integer,Integer>)s;
163     timer.start(nm, n);
164     for (int i = 0; i < n; i++) {
165     if ((Integer)(intMap.get(i)) != i) ++sum;
166 dl 1.1 }
167     timer.finish();
168 dl 1.4 reallyAssert (sum == expect);
169 dl 1.1 }
170    
171 dl 1.4 static void remTest(String nm, int n, Map s, Object[] key, int expect) {
172 dl 1.1 int sum = 0;
173     timer.start(nm, n);
174     for (int i = 0; i < n; i++) {
175     if (s.remove(key[i]) != null) ++sum;
176     }
177     timer.finish();
178     reallyAssert (sum == expect);
179 dl 1.4 checkSum += sum;
180     }
181    
182     static void clrTest(int n, Map s) {
183     String nm = "Remove Present ";
184     timer.start(nm, n);
185     s.clear();
186     timer.finish();
187     reallyAssert (s.isEmpty());
188 dl 1.1 }
189    
190 dl 1.4 static void putTest(String nm, int n, Map s, Object[] key, int expect) {
191 dl 1.1 int sum = 0;
192     timer.start(nm, n);
193     for (int i = 0; i < n; i++) {
194 dl 1.4 Object k = key[i];
195     Object v = s.put(k, k);
196     if (v == null) ++sum;
197 dl 1.1 }
198     timer.finish();
199     reallyAssert (sum == expect);
200 dl 1.4 checkSum += sum;
201 dl 1.1 }
202    
203 dl 1.4 static void keyTest(String nm, int n, Map s, Object[] key, int expect) {
204 dl 1.1 int sum = 0;
205     timer.start(nm, n);
206     for (int i = 0; i < n; i++) {
207     if (s.containsKey(key[i])) ++sum;
208     }
209     timer.finish();
210     reallyAssert (sum == expect);
211 dl 1.4 checkSum += sum;
212     }
213    
214     // version without timing for uncategorized tests
215     static void untimedKeyTest(String nm, int n, Map s, Object[] key, int expect) {
216     int sum = 0;
217     for (int i = 0; i < n; i++) {
218     if (s.containsKey(key[i])) ++sum;
219     }
220     reallyAssert (sum == expect);
221     checkSum += sum;
222 dl 1.1 }
223    
224 dl 1.4 static void remHalfTest(String nm, int n, Map s, Object[] key, int expect) {
225 dl 1.1 int sum = 0;
226     timer.start(nm, n/2);
227     for (int i = n-2; i >= 0; i-=2) {
228     if (s.remove(key[i]) != null) ++sum;
229     }
230     timer.finish();
231     reallyAssert (sum == expect);
232 dl 1.4 checkSum += sum;
233 dl 1.1 }
234    
235 dl 1.4 static void valTest(Map s, Object[] key) {
236     int size = s.size();
237 dl 1.1 int sum = 0;
238 dl 1.4 timer.start("Traverse access key/val", size);
239     if (s.containsValue(MISSING)) ++sum;
240 dl 1.1 timer.finish();
241 dl 1.4 reallyAssert (sum == 0);
242     checkSum += sum;
243 dl 1.1 }
244    
245 dl 1.4
246     static Object kitTest(Map s, int size) {
247     Object last = null;
248 dl 1.1 int sum = 0;
249 dl 1.4 timer.start("Traverse access key/val", size);
250     for (Iterator it = s.keySet().iterator(); it.hasNext(); ) {
251     Object x = it.next();
252     if (x != last && x != null && x.getClass() == eclass)
253     ++sum;
254     last = x;
255 dl 1.1 }
256     timer.finish();
257 dl 1.4 reallyAssert (sum == size);
258     checkSum += sum;
259     return last;
260 dl 1.1 }
261    
262 dl 1.4 static Object vitTest(Map s, int size) {
263     Object last = null;
264 dl 1.1 int sum = 0;
265 dl 1.4 timer.start("Traverse access key/val", size);
266     for (Iterator it = s.values().iterator(); it.hasNext(); ) {
267     Object x = it.next();
268     if (x != last && x != null && x.getClass() == eclass)
269     ++sum;
270     last = x;
271 dl 1.1 }
272     timer.finish();
273 dl 1.4 reallyAssert (sum == size);
274     checkSum += sum;
275     return last;
276 dl 1.1 }
277    
278 dl 1.4 static void eitTest(Map s, int size) {
279 dl 1.1 int sum = 0;
280 dl 1.4 timer.start("Traverse access entry ", size);
281     for (Iterator it = s.entrySet().iterator(); it.hasNext(); ) {
282     Map.Entry e = (Map.Entry)it.next();
283     Object k = e.getKey();
284     Object v = e.getValue();
285     if (k != null && k.getClass() == eclass &&
286     v != null && v.getClass() == eclass)
287     ++sum;
288     }
289 dl 1.1 timer.finish();
290 dl 1.4 reallyAssert (sum == size);
291     checkSum += sum;
292 dl 1.1 }
293    
294 dl 1.4 static void itRemTest(Map s, int size) {
295     int sz = s.size();
296     reallyAssert (sz == size);
297     timer.start("Remove Present ", size);
298 dl 1.1 int sum = 0;
299 dl 1.4 for (Iterator it = s.keySet().iterator(); it.hasNext(); ) {
300     it.next();
301     it.remove();
302     ++sum;
303 dl 1.1 }
304     timer.finish();
305 dl 1.4 reallyAssert (sum == sz);
306     checkSum += sum;
307 dl 1.1 }
308    
309 dl 1.4 static void itHalfRemTest(Map s, int size) {
310     int sz = s.size();
311     reallyAssert (sz == size);
312     timer.start("Remove Present ", size);
313 dl 1.1 int sum = 0;
314     for (Iterator it = s.keySet().iterator(); it.hasNext(); ) {
315 dl 1.4 it.next();
316     it.remove();
317     if (it.hasNext())
318     it.next();
319     ++sum;
320 dl 1.1 }
321     timer.finish();
322 dl 1.4 reallyAssert (sum == sz / 2);
323     checkSum += sum;
324     }
325    
326     static void putAllTest(String nm, int n, Map src, Map dst) {
327     timer.start(nm, n);
328     dst.putAll(src);
329     timer.finish();
330     reallyAssert (src.size() == dst.size());
331     }
332    
333     static void serTest(Map s, int size) throws Exception {
334     if (!(s instanceof Serializable))
335     return;
336     System.out.print("Serialize : ");
337    
338     for (int i = 0; i < size; i++) {
339     s.put(new Integer(i), Boolean.TRUE);
340     }
341    
342     long startTime = System.currentTimeMillis();
343    
344     FileOutputStream fs = new FileOutputStream("MapCheck.dat");
345     ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(fs));
346     out.writeObject(s);
347     out.close();
348    
349     FileInputStream is = new FileInputStream("MapCheck.dat");
350     ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(is));
351     Map m = (Map)in.readObject();
352    
353     long endTime = System.currentTimeMillis();
354     long time = endTime - startTime;
355    
356     System.out.print(time + "ms");
357    
358     if (s instanceof IdentityHashMap) return;
359     reallyAssert (s.equals(m));
360 dl 1.1 }
361    
362 dl 1.4 static void mainTest(Map s, Object[] key, Object[] absent) {
363     int size = key.length;
364    
365     putTest("Add Absent ", size, s, key, size);
366     reallyAssert(s.size() == size);
367     getTest("Access Present ", size, s, key, size);
368     getTest("Search Absent ", size, s, absent, 0);
369     kitTest(s, size);
370     vitTest(s, size);
371     eitTest(s, size);
372     putTest("Modify Present ", size, s, key, 0);
373     reallyAssert(s.size() == size);
374     untimedKeyTest("Access Present ", size, s, key, size);
375     keyTest("Search Absent ", size, s, absent, 0);
376     valTest(s, key);
377     remTest("Search Absent ", size, s, absent, 0);
378     reallyAssert(s.size() == size);
379     remHalfTest("Remove Present ", size, s, key, size / 2);
380     reallyAssert(s.size() == size / 2);
381     getTest("Access Present ", size, s, key, size / 2);
382     putTest("Add Absent ", size, s, key, size / 2);
383     reallyAssert(s.size() == size);
384     getTest("Access Present ", size, s, key, size);
385     getTest("Search Absent ", size, s, absent, 0);
386     itRemTest(s, size);
387     putTest("Add Absent ", size, s, key, size);
388     reallyAssert(s.size() == size);
389     getTest("Access Present ", size, s, key, size);
390     untimedKeyTest("Access Present ", size, s, key, size);
391     kitTest(s, size);
392     vitTest(s, size);
393     eitTest(s, size);
394     twoMapTest1(s, key, absent);
395     twoMapTest2(s, key, absent);
396     }
397    
398     static void twoMapTest1(Map s, Object[] key, Object[] absent) {
399     int size = s.size();
400     Map s2 = newMap();
401     putAllTest("Add Absent ", size, s, s2);
402     getTest("Access Present ", size, s2, key, size);
403     itHalfRemTest(s2, size);
404     reallyAssert(s2.size() == size / 2);
405     itHalfRemTest(s2, size / 2);
406     reallyAssert(s2.size() == size / 4);
407     putTest("Add Absent ", size, s2, absent, size);
408     putTest("Add Absent ", size, s2, key, size * 3 / 4);
409     reallyAssert(s2.size() == size * 2);
410     clrTest(size, s2);
411     }
412    
413     static void twoMapTest2(Map s, Object[] key, Object[] absent) {
414     int size = key.length;
415    
416     Map s2 = newMap();
417     putAllTest("Add Absent ", size, s, s2);
418     putAllTest("Modify Present ", size, s, s2);
419    
420     Object lastkey = kitTest(s2, size);
421     Object hold = s2.get(lastkey);
422 dl 1.1 int sum = 0;
423 dl 1.4
424     timer.start("Traverse access entry ", size * 12); // 12 until finish
425    
426     int sh1 = s.hashCode() - s2.hashCode();
427     reallyAssert (sh1 == 0);
428    
429     boolean eq1 = s2.equals(s);
430     boolean eq2 = s.equals(s2);
431     reallyAssert (eq1 && eq2);
432    
433     Set es2 = s2.entrySet();
434     for (Iterator it = s.entrySet().iterator(); it.hasNext(); ) {
435     Object entry = it.next();
436     if (es2.contains(entry)) ++sum;
437 dl 1.1 }
438     reallyAssert (sum == size);
439 dl 1.4
440     s2.put(lastkey, MISSING);
441    
442     int sh2 = s.hashCode() - s2.hashCode();
443     reallyAssert (sh2 != 0);
444    
445     eq1 = s2.equals(s);
446     eq2 = s.equals(s2);
447     reallyAssert (!eq1 && !eq2);
448    
449     sum = 0;
450 dl 1.1 for (Iterator it = s.entrySet().iterator(); it.hasNext(); ) {
451 dl 1.3 Map.Entry e = (Map.Entry)it.next();
452 dl 1.4 e.setValue(absent[sum++]);
453     }
454     reallyAssert (sum == size);
455     for (Iterator it = s2.entrySet().iterator(); it.hasNext(); ) {
456     Map.Entry e = (Map.Entry)it.next();
457     e.setValue(s.get(e.getKey()));
458     }
459    
460     timer.finish();
461    
462     int rmiss = 0;
463     timer.start("Remove Present ", size * 2);
464     Iterator s2i = s2.entrySet().iterator();
465     Set es = s.entrySet();
466     while (s2i.hasNext()) {
467     if (!es.remove(s2i.next()))
468     ++rmiss;
469 dl 1.1 }
470     timer.finish();
471 dl 1.4 reallyAssert(rmiss == 0);
472    
473     clrTest(size, s2);
474     reallyAssert (s2.isEmpty() && s.isEmpty());
475 dl 1.1 }
476    
477 dl 1.4 static void itTest4(Map s, int size, int pos) {
478 dl 1.1 IdentityHashMap seen = new IdentityHashMap(size);
479     reallyAssert (s.size() == size);
480     int sum = 0;
481     timer.start("Iter XEntry ", size);
482     Iterator it = s.entrySet().iterator();
483     Object k = null;
484     Object v = null;
485     for (int i = 0; i < size-pos; ++i) {
486     Map.Entry x = (Map.Entry)(it.next());
487     k = x.getKey();
488     v = x.getValue();
489     seen.put(k, k);
490     if (x != MISSING)
491     ++sum;
492     }
493     reallyAssert (s.containsKey(k));
494     it.remove();
495     reallyAssert (!s.containsKey(k));
496     while (it.hasNext()) {
497     Map.Entry x = (Map.Entry)(it.next());
498     Object k2 = x.getKey();
499     seen.put(k2, k2);
500     if (x != MISSING)
501     ++sum;
502     }
503    
504     reallyAssert (s.size() == size-1);
505     s.put(k, v);
506     reallyAssert (seen.size() == size);
507     timer.finish();
508     reallyAssert (sum == size);
509     reallyAssert (s.size() == size);
510     }
511    
512    
513    
514 dl 1.4 static void initializeKeys(Object[] key, Object[] absent, int size) {
515     if (eclass == Object.class) {
516     for (int i = 0; i < size; ++i) key[i] = new Object();
517     for (int i = 0; i < size; ++i) absent[i] = new Object();
518     }
519     else if (eclass == Integer.class) {
520     initInts(key, absent, size);
521     }
522     else if (eclass == Float.class) {
523     initFloats(key, absent, size);
524     }
525     else if (eclass == String.class) {
526     initWords(size, key, absent);
527     }
528     else
529     throw new Error("unknown type");
530     }
531    
532     static void initInts(Object[] key, Object[] absent, int size) {
533     for (int i = 0; i < size; ++i)
534     key[i] = Integer.valueOf(i);
535     Map m = newMap();
536     int k = 0;
537     while (k < size) {
538     int r = srng.next();
539     if (r < 0 || r >= size) {
540     Integer ir = Integer.valueOf(r);
541     if (m.put(ir, ir) == null)
542     absent[k++] = ir;
543     }
544 dl 1.1 }
545     }
546    
547 dl 1.4 static void initFloats(Object[] key, Object[] absent, int size) {
548     Map m = newMap();
549 dl 1.1 for (int i = 0; i < size; ++i) {
550 dl 1.4 float r = Float.valueOf(i);
551     key[i] = r;
552     m.put(r, r);
553 dl 1.1 }
554 dl 1.4 int k = 0;
555     while (k < size) {
556     float r = rng.nextFloat();
557     Float ir = Float.valueOf(r);
558     if (m.put(ir, ir) == null)
559     absent[k++] = ir;
560 dl 1.1 }
561     }
562    
563 dl 1.4 // Use as many real words as possible, then use fake random words
564 dl 1.1
565 dl 1.4 static void initWords(int size, Object[] key, Object[] abs) {
566     String fileName = "testwords.txt";
567     int ki = 0;
568     int ai = 0;
569     try {
570     FileInputStream fr = new FileInputStream(fileName);
571     BufferedInputStream in = new BufferedInputStream(fr);
572     while (ki < size || ai < size) {
573     StringBuffer sb = new StringBuffer();
574     for (;;) {
575     int c = in.read();
576     if (c < 0) {
577     if (ki < size)
578     randomWords(key, ki, size);
579     if (ai < size)
580     randomWords(abs, ai, size);
581     in.close();
582     return;
583     }
584     if (c == '\n') {
585     String s = sb.toString();
586     if (ki < size)
587     key[ki++] = s;
588     else
589     abs[ai++] = s;
590     break;
591     }
592     sb.append((char)c);
593     }
594     }
595     in.close();
596 dl 1.1 }
597 dl 1.4 catch (IOException ex) {
598     System.out.println("Can't read words file:" + ex);
599     throw new Error(ex);
600 dl 1.1 }
601     }
602    
603    
604 dl 1.4 static void randomWords(Object[] ws, int origin, int size) {
605     for (int i = origin; i < size; ++i) {
606     int k = 0;
607     int len = 2 + (srng.next() & 0xf);
608     char[] c = new char[len * 4 + 1];
609     for (int j = 1; j < len; ++j) {
610     int r = srng.next();
611     c[k++] = (char)(' ' + (r & 0x7f));
612     r >>>= 8;
613     c[k++] = (char)(' ' + (r & 0x7f));
614     r >>>= 8;
615     c[k++] = (char)(' ' + (r & 0x7f));
616     r >>>= 8;
617     c[k++] = (char)(' ' + (r & 0x7f));
618     }
619     c[k++] = (char)((i & 31) | 1); // never == to any testword
620     ws[i] = new String(c);
621 dl 1.1 }
622     }
623    
624 dl 1.4 static final class TestTimer {
625 dl 1.1 private String name;
626     private long numOps;
627     private long startTime;
628    
629     static final java.util.TreeMap accum = new java.util.TreeMap();
630    
631     static void printStats() {
632     for (Iterator it = accum.entrySet().iterator(); it.hasNext(); ) {
633     Map.Entry e = (Map.Entry)(it.next());
634     Stats stats = ((Stats)(e.getValue()));
635 dl 1.4 System.out.print(e.getKey() + ": ");
636     long s;
637     long n = stats.number;
638     if (n == 0) {
639     n = stats.firstn;
640     s = stats.first;
641     }
642     else
643     s = stats.sum;
644    
645     double t = ((double)s) / n;
646     long nano = Math.round(t);
647     System.out.printf("%6d", + nano);
648     System.out.println();
649 dl 1.1 }
650     }
651    
652     void start(String name, long numOps) {
653     this.name = name;
654     this.numOps = numOps;
655 dl 1.4 startTime = System.nanoTime();
656 dl 1.1 }
657    
658     void finish() {
659 dl 1.4 long elapsed = System.nanoTime() - startTime;
660 dl 1.1 Object st = accum.get(name);
661     if (st == null)
662 dl 1.4 accum.put(name, new Stats(elapsed, numOps));
663     else
664     ((Stats)st).addTime(elapsed, numOps);
665 dl 1.1 }
666    
667     }
668    
669 dl 1.4 static final class Stats {
670     long sum;
671     long number;
672     long first;
673     long firstn;
674     Stats(long t, long n) {
675     first = t;
676     firstn = n;
677     }
678     void addTime(long t, long n) {
679     sum += t;
680     number += n;
681     }
682 dl 1.1 }
683    
684    
685     static void shuffle(Object[] keys) {
686     int size = keys.length;
687 dl 1.4 for (int i= size; i>1; i--) {
688 dl 1.1 int r = rng.nextInt(i);
689     Object t = keys[i-1];
690     keys[i-1] = keys[r];
691     keys[r] = t;
692     }
693     }
694    
695 dl 1.4 static void shuffle(ArrayList keys) {
696     int size = keys.size();
697     for (int i= size; i>1; i--) {
698     int r = rng.nextInt(i);
699     Object t = keys.get(i-1);
700     keys.set(i-1, keys.get(r));
701     keys.set(r, t);
702     }
703     }
704    
705 dl 1.1 }
706