/******************************************************************************* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ /** * File: TreeMapAPITest.java * Package: net.adoptopenjdk.test.util.treemap */ package net.adoptopenjdk.test.util.treemap; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.ArrayList; import java.util.Vector; import junit.framework.TestCase; /** * This tests execises the functions available in the * TreeMap API (Java 5) */ public class TreeMapAPITest extends TestCase { public void setUp() { } /*** * Tests creating TreeMaps with the Comparator constructor */ public void testComparatorConstructor() { // create a TreeMap with the custom comparator TreeMapKeyComparator comparator = new TreeMapKeyComparator(); TreeMap<TreeMapKey,Object> tree = new TreeMap<TreeMapKey,Object>(comparator); // populate tree TreeMapFactory.populateTreeWithKey(tree, TreeMapFactory.SIZE); assertEquals(tree.size(), TreeMapFactory.SIZE); // check that they're in the correct order now checkOrderedTMK(tree); // change the comparator to order the other way comparator.reverse(); // check it's ordered correctly checkOrderedTMK(tree); } /*** * Tests creating TreeMaps with the Map constructor */ public void testMapConstructor() { // create a hashmap HashMap<Integer,Object> hashmap1 = new HashMap<Integer,Object>(); for (int i=(TreeMapFactory.SIZE / 2) - 1; i>=0; i--) { hashmap1.put(new Integer(i), new Object()); } // create a TreeMap from the map TreeMap<Integer,Object> tree1 = new TreeMap<Integer,Object>(hashmap1); assertEquals(tree1.size(), TreeMapFactory.SIZE/2); checkOrdered(tree1); /* add some new elements to the HashMap and TreeMap * should now contain all numbers 0..TreeMapFactory.SIZE */ for (int i=TreeMapFactory.SIZE / 2; i<TreeMapFactory.SIZE; i++) { hashmap1.put(new Integer(i), new Object()); tree1.put(new Integer(i), new Object()); } // and remove some entries - remove every element mod 5 for (int i=0; i<TreeMapFactory.SIZE; i=i+5) { hashmap1.remove(new Integer(i)); tree1.remove(new Integer(i)); } // check the expected values are in the TreeMap for (int i=0; i<TreeMapFactory.SIZE; i++) { Integer tempInteger = new Integer(i); // check that the hashmap and tree contain the same String message = hashmap1.containsKey(tempInteger) ? "Hashmap contains key " + i + " but TreeMap doesn't": "Hashmap doesn't contain key " + i + " but TreeMap does"; assertEquals(message, hashmap1.containsKey(tempInteger), tree1.containsKey(tempInteger)); if (i % 5 == 0) { assertFalse("Tree doesn't contain key " + i, tree1.containsKey(tempInteger)); } else { assertTrue("Tree shouldn't contain key " + i, tree1.containsKey(tempInteger)); } } // and that it's still ordered checkOrdered(tree1); // error checking - put a null in the Map hashmap1 = new HashMap<Integer,Object>(); hashmap1.put(new Integer(0), new Object()); hashmap1.put(new Integer(2), new Object()); hashmap1.put(null, new Object()); hashmap1.put(new Integer(1), new Object()); try { tree1 = new TreeMap<Integer,Object>(hashmap1); fail("Attempt to create TreeMap with a HashMap containing a null key should throw a NullPointerException"); } catch (NullPointerException e) { // correct behaviour } // create a map where the keys don't implement Comparable HashMap<Object,Object> hashmap2 = new HashMap<Object,Object>(); hashmap2.put(new Object(), new Object()); hashmap2.put(new Object(), new Object()); try { new TreeMap<Object,Object>(hashmap2); fail("Attempt to create TreeMap with keys that don't implement Comparable should throw ClassCastException"); } catch (ClassCastException e) { // correct behaviour } } public void testClone() { // create a TreeMap TreeMap<Integer,Object> treemap = new TreeMap<Integer,Object>(); for (int i=0; i<TreeMapFactory.SIZE; i++) { treemap.put(new Integer(i), new MapObject(i)); } // clone it @SuppressWarnings("unchecked") TreeMap<Integer, Object> treemap_clone = (TreeMap<Integer, Object>)treemap.clone(); // test equal assertEquals("Cloned array is not equal to original", treemap, treemap_clone); // test shallow equality by changing some of the object properties for (int i=0; i<TreeMapFactory.SIZE; i=i+10) { ((MapObject)treemap.get(new Integer(i))).value += 10; } assertEquals("Treemaps should contain the same keys", treemap.keySet(), treemap_clone.keySet()); assertEquals("TreeMaps not shallowly equal", treemap, treemap_clone); } public void testComparator() { // create a TreeMap TreeMap<TreeMapKey,Object> tree = TreeMapFactory.getTreeWithKey(TreeMapFactory.SIZE); // get comparator Comparator<?> c = tree.comparator(); // check it's null assertNull("comparator() should return null as the tree is using the natural ordering of its keys", c); // create a tree with an explicit Comparator tree = new TreeMap<TreeMapKey,Object>(new TreeMapKeyComparator()); TreeMapFactory.populateTreeWithKey(tree, TreeMapFactory.SIZE); // get comparator c = tree.comparator(); // check not null assertNotNull("comparator() returned null when Comparator has been set", c); } public void testContainsKey() { // create an empty tree map TreeMap<TreeMapKey,Object> tree = new TreeMap<TreeMapKey,Object>(); // look for a key assertFalse("Found a key in an empty tree", tree.containsKey(new TreeMapKey(2))); // create a list of ints 0..TreeMapFactory.SIZE-1 in random order ArrayList<Integer> intList = new ArrayList<Integer>(TreeMapFactory.SIZE); for (int i=0; i<TreeMapFactory.SIZE; i++) { intList.add(new Integer(i)); } Collections.shuffle(intList, new Random(173417)); // add keys to tree while (intList.size()>0) { tree.put(new TreeMapKey(((Integer)intList.remove(0)).intValue()), null); } // check that the tree contains all keys from 0 to TreeMapFactory.SIZE-1 for (int i=0; i<TreeMapFactory.SIZE; i++) { assertTrue("Tree doesn't contain key " + i, tree.containsKey(new TreeMapKey(i))); } // try null key try { tree.containsKey(null); fail("Attempt to call TreeMap.containsKey with null should throw NullPointerException"); } catch (NullPointerException e) { // expected behaviour } } public void testContainsValue() { TreeMap<Integer,String> tree = new TreeMap<Integer,String>(); // look for a null value assertFalse("Looking for null value in an empty tree", tree.containsValue(null)); // look for a non-null value assertFalse("Looking for non-null value in an empty tree", tree.containsValue(Integer.toString(10))); // put some values in the tree Random random = new Random(23412849); Integer k = null; for (int i=0; i<TreeMapFactory.SIZE; i++) { // find a key that isn't in the tree already do { k = new Integer(random.nextInt()); } while (tree.containsKey(k)); // put a String value in the tree tree.put(k, Integer.toString(i)); } // look for each string for (int i=0; i<TreeMapFactory.SIZE; i++) { assertTrue("Tree doesn't contain value " + i, tree.containsValue(Integer.toString(i))); } // look for one that shouldn't be there assertFalse("Tree contains value " + TreeMapFactory.SIZE, tree.containsValue(Integer.toString(TreeMapFactory.SIZE))); // look for a null key assertFalse("Looking for null value in tree that doesn't contain one", tree.containsValue(null)); // put a null in the tree k = new Integer(random.nextInt()); tree.put(k, null); assertTrue("Looking for null value in tree that contains one", tree.containsValue(null)); } public void testEntrySet() { TreeMap<Integer,Integer> tree = new TreeMap<Integer,Integer>(); assertEquals("EntrySet of empty TreeMap isn't empty", tree.entrySet().size(), 0); // populate tree with random keys TreeMapFactory.populateTree(tree,TreeMapFactory.SIZE); // get the entry set Set<?> entrySet = tree.entrySet(); assertEquals("EntrySet has " + entrySet.size() + " entries, expected: " + TreeMapFactory.SIZE, entrySet.size(), TreeMapFactory.SIZE); Iterator<?> entrySetIterator = entrySet.iterator(); Integer previous = (Integer)(((Map.Entry<?,?>)entrySetIterator.next()).getKey()); while (entrySetIterator.hasNext()) { Integer current = (Integer)(((Map.Entry<?,?>)entrySetIterator.next()).getKey()); assertTrue("Key " + previous.toString() + " occurs before key " + current, previous.compareTo(current) <= 0); previous = current; } /* * we should be able to remove mappings in both the Iterator and the Set * and have them reflected in the tree * reset the iterator */ entrySetIterator = entrySet.iterator(); boolean toRemove = true; Vector<Object> removedKeys = new Vector<Object>(); Vector<Object> keptEntries = new Vector<Object>(); // remove some entries via Iterator.remove while(entrySetIterator.hasNext()) { Object entry = entrySetIterator.next(); // alternate between removing via Iterator.remove if (toRemove) { removedKeys.add(((Map.Entry<?,?>)entry).getKey()); entrySetIterator.remove(); } else { keptEntries.add(entry); } toRemove = !toRemove; } // remove some entries via entrySet.remove Vector<Object> keptKeys = new Vector<Object>(); for (int i=0; i<keptEntries.size(); i++) { Object entry = keptEntries.elementAt(i); if (toRemove) { removedKeys.add(((Map.Entry<?,?>)entry).getKey()); assertTrue("Set.remove didn't remove entry " + entry, entrySet.remove(entry)); } else { keptKeys.add(((Map.Entry<?,?>)entry).getKey()); } toRemove = !toRemove; } // check removed entries aren't in tree for(int i=0; i<removedKeys.size(); i++) { Object key = removedKeys.elementAt(i); assertFalse("Check " + i + " failed: tree contains key " + key + " which was removed", tree.containsKey(key)); } // check other entires are still in the tree assertEquals(keptKeys.size(), tree.size()); for (int i=0; i<keptKeys.size(); i++) { Object key = keptKeys.elementAt(i); assertTrue("Check " + i + " failed: tree doesn't contain key " + key, tree.containsKey(key)); } } public void testFirstKey() { // create two random treemaps and retrieve their first keys TreeMap<Integer,Integer> tree1 = TreeMapFactory.getTree(TreeMapFactory.SIZE/2); Comparable<Integer> firstkey1 = (Comparable<Integer>)tree1.firstKey(); TreeMap<Integer,Integer> tree2 = TreeMapFactory.getTree(TreeMapFactory.SIZE/2); Comparable<Integer> firstkey2 = (Comparable<Integer>)tree2.firstKey(); // join them together and check the first key is correct tree1.putAll(tree2); if (firstkey1.compareTo((Integer)firstkey2) <= 0) { assertEquals(firstkey1, tree1.firstKey()); } else { assertEquals(firstkey2, tree1.firstKey()); } // put the smallest Integer in and see if it's the last key tree1.put(new Integer(Integer.MIN_VALUE), null); assertEquals(new Integer(Integer.MIN_VALUE), tree1.firstKey()); } public void testLastKey() { // create two random treemaps and retrieve their last keys TreeMap<Integer,Integer> tree1 = TreeMapFactory.getTree(TreeMapFactory.SIZE/2); Comparable<Integer> lastkey1 = (Comparable<Integer>)tree1.lastKey(); TreeMap<Integer,Integer> tree2 = TreeMapFactory.getTree(TreeMapFactory.SIZE/2); Comparable<Integer> lastkey2 = (Comparable<Integer>)tree2.lastKey(); // join them together and check the last key is correct tree1.putAll(tree2); if (lastkey1.compareTo((Integer)lastkey2) >= 0) { assertEquals(lastkey1, tree1.lastKey()); } else { assertEquals(lastkey2, tree1.lastKey()); } // put the largest Integer in and see if it's the last key tree1.put(new Integer(Integer.MAX_VALUE), null); assertEquals(new Integer(Integer.MAX_VALUE), tree1.lastKey()); } public void testHeadMap() { TreeMap<Integer,Integer> tree = TreeMapFactory.getTree(TreeMapFactory.SIZE); // should give the same map back minus the last Key Map<Integer,Integer> headmap = tree.headMap(tree.lastKey()); tree.remove(tree.lastKey()); assertEquals("tree.headmap(tree.lastKey()) is not equal to tree.remove(tree.lastKey())", headmap, tree); // split map headmap = tree.headMap(new Integer(0)); // try inserting a key greater than 0 try { headmap.put(new Integer(1), null); fail("Inserting key 1 into a head map containing integers under 0, should throw" + "an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } // try inserting 0 try { headmap.put(new Integer(0), null); fail("Inserting key 0 into a head map containing integers under 0, should throw" + "an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } } public void testKeySet() { TreeMap<Integer,Integer> tree = new TreeMap<Integer,Integer>(); assertEquals("EntrySet of empty TreeMap isn't empty", tree.keySet().size(), 0); // populate tree with random keys TreeMapFactory.populateTree(tree,TreeMapFactory.SIZE); // get the entry set Set<Integer> keySet = tree.keySet(); assertEquals("KeySet has " + keySet.size() + " keys, expected: " + TreeMapFactory.SIZE, keySet.size(), TreeMapFactory.SIZE); Iterator<Integer> keySetIterator = keySet.iterator(); Integer previous = (Integer)(keySetIterator.next()); while (keySetIterator.hasNext()) { Integer current = (Integer)(keySetIterator.next()); assertTrue("Key " + previous.toString() + " occurs before key " + current, previous.compareTo(current) <= 0); previous = current; } /* * we should be able to remove mappings in both the Iterator and the Set * and have them reflected in the tree * reset the iterator */ keySetIterator = keySet.iterator(); boolean toRemove = true; Vector<Integer> removedKeys = new Vector<Integer>(); Vector<Integer> keptEntries = new Vector<Integer>(); // remove some entries via Iterator.remove while(keySetIterator.hasNext()) { Integer key = (Integer)keySetIterator.next(); // alternate between removing via Iterator.remove if (toRemove) { removedKeys.add(key); keySetIterator.remove(); } else { keptEntries.add(key); } toRemove = !toRemove; } // remove some entries via entrySet.remove Vector<Integer> keptKeys = new Vector<Integer>(); for (int i=0; i<keptEntries.size(); i++) { Integer key = (Integer) keptEntries.elementAt(i); if (toRemove) { removedKeys.add(key); assertTrue("Set.remove didn't remove entry " + key, keySet.remove(key)); } else { keptKeys.add(key); } toRemove = !toRemove; } // check removed entries aren't in tree for(int i=0; i<removedKeys.size(); i++) { Object key = removedKeys.elementAt(i); assertFalse("Check " + i + " failed: tree contains key " + key + " which was removed", tree.containsKey(key)); } // check other entires are still in the tree assertEquals(keptKeys.size(), tree.size()); for (int i=0; i<keptKeys.size(); i++) { Object key = keptKeys.elementAt(i); assertTrue("Check " + i + " failed: tree doesn't contain key " + key, tree.containsKey(key)); } } public void testGetPutRemove() { TreeMap<Integer,Integer> tree1 = TreeMapFactory.getTree(TreeMapFactory.SIZE); try { tree1.put(null,null); fail("Attempt to put null in tree should throw NullPointerException"); } catch(NullPointerException e) { // expected behaviour } TreeMap<Integer,String> tree2 = new TreeMap<Integer,String>(); // test overwriting the same key String[] values = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; int[] keys = {0, 1231, -5423980, 13434, 8537, 18394, -849580, 923842, 93, 234890}; for (int i=0; i<keys.length; i++) { for (int j=0; j<values.length; j++) { tree2.put(new Integer(keys[i]), values[j]); } } for (int i=0; i<keys.length; i++) { assertEquals("Key " + keys[i] + " is not mapped to expected value", tree2.get(keys[i]), values[values.length-1]); } // create a HashMap with the same mappings HashMap<Integer,String> hashmap2 = new HashMap<Integer,String>(tree2); // make some changes to both Iterator<Integer> keysetIterator = tree2.keySet().iterator(); int frequency = 0; while (keysetIterator.hasNext()) { Integer key = keysetIterator.next(); if (frequency % 10 == 0) { // remove the entry hashmap2.remove(key); // remove via the iterator otherwise will get a ConcurrentModificationException keysetIterator.remove(); } else if (frequency % 4 == 1) { // swap some values around if (!keysetIterator.hasNext()) break; Integer key2 = keysetIterator.next(); // do the same swap in the hashmap and tree String temp = hashmap2.get(key); hashmap2.put(key, hashmap2.get(key2)); hashmap2.put(key2, temp); temp = tree2.get(key); tree2.put(key, tree2.get(key2)); tree2.put(key2, temp); } else if (frequency % 10 == 3) { // reassociate that key to a different value hashmap2.put(key, null); tree2.put(key, null); } frequency++; } // add some extra entries Random random = new Random(1438908); for (int i=0; i<TreeMapFactory.SIZE/2; i++) { Integer key = new Integer(random.nextInt()); hashmap2.put(key, null); tree2.put(key, null); } // check the tree's still ordered checkOrdered(tree2); // check they've the same mappings assertEquals("HashMap and TreeMap differ after swappings", hashmap2, tree2); } public void testPutAll() { TreeMap<Integer,Integer> treemap = TreeMapFactory.getTree(TreeMapFactory.SIZE); // create a hashmap Random random = new Random(7987); HashMap<Integer,Integer> hashmap = new HashMap<Integer,Integer>(); int unique = 0; while (hashmap.size() <= TreeMapFactory.SIZE) { Integer key = new Integer(random.nextInt()); hashmap.put(key, null); if (!treemap.containsKey(key)) { unique++; } } // put all entries in the tree treemap.putAll(hashmap); checkOrdered(treemap); // size of tree should be TreeMapFactory.SIZE + unique assertEquals(treemap.size(), TreeMapFactory.SIZE + unique); } public void testSubMap() { TreeMap<Integer,Integer> tree = TreeMapFactory.getTree(TreeMapFactory.SIZE); // should give the same map back minus the last Key Map<Integer,Integer> submap = tree.subMap(tree.firstKey(), tree.lastKey()); tree.remove(tree.firstKey()); tree.remove(tree.lastKey()); assertEquals("tree.submap(tree.firstKey, tree.lastKey()) is not equal to tree.remove(tree.firstKey());tree.remove(tree.lastKey())", submap, tree); // split map submap = tree.subMap(tree.firstKey(), new Integer(0)); // try inserting a key greater than 0 try { submap.put(new Integer(1), null); fail("Inserting key 1 into a head map containing integers under 0, should throw" + " an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } // try inserting 0 try { submap.put(new Integer(0), null); fail("Inserting key 0 into a sub map containing integers under 0, should throw" + " an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } // split the other way submap = tree.subMap(new Integer(1), tree.lastKey()); // try inserting a key greater than 0 try { submap.put(new Integer(-1), null); fail("Inserting key -1 into a head map containing integers over 0, should throw" + " an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } // try inserting 0 try { submap.put(new Integer(0), null); fail("Inserting key 0 into a sub map containing integers over 0, should throw" + " an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } } public void testTailMap() { TreeMap<Integer,Integer> tree = TreeMapFactory.getTree(TreeMapFactory.SIZE); // should give the same map back minus the first Key Map<Integer,Integer> tailmap = tree.tailMap(tree.firstKey()); tree.remove(tree.firstKey()); assertEquals("tree.tailmap(tree.firstKey()) is not equal to tree.remove(tree.lastKey())", tailmap, tree); // split map tailmap = tree.tailMap(new Integer(1)); // try inserting a key less than 0 try { tailmap.put(new Integer(-1), null); fail("Inserting key -1 into a tail map containing integers over 0, should throw" + " an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } // try inserting 0 try { tailmap.put(new Integer(0), null); fail("Inserting key 0 into a tail map containing integers over 0, should throw" + " an IllegalArgumentException"); } catch(IllegalArgumentException e) { // expected behaviour } } private void checkOrdered(SortedMap<Integer,?> tree) { Iterator<Integer> iterator = tree.keySet().iterator(); if (!iterator.hasNext()) { return; } Integer previous = (Integer)iterator.next(); while (iterator.hasNext()) { Integer current = (Integer)iterator.next(); assertTrue("key " + previous + " occurs before key " + current + " in TreeMap", current.compareTo(previous) >= 0); previous = current; } } private void checkOrderedTMK(SortedMap<TreeMapKey,?> tree) { Iterator<TreeMapKey> iterator = tree.keySet().iterator(); if (!iterator.hasNext()) { return; } TreeMapKey previous = (TreeMapKey)iterator.next(); while (iterator.hasNext()) { TreeMapKey current = (TreeMapKey)iterator.next(); assertTrue("key " + previous + " occurs before key " + current + " in TreeMap", current.compareTo(previous) >= 0); previous = current; } } /** * Test object for putting in TreeMaps */ class MapObject { public int value; public MapObject(int value) { this.value = value; } } }