001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.util; 014 015import java.util.Iterator; 016import java.util.NoSuchElementException; 017 018import org.jikesrvm.VM; 019import org.jikesrvm.mm.mminterface.MemoryManager; 020 021/** 022 * Common super class for all VM hash sets. 023 */ 024abstract class AbstractHashSetRVM<T> implements Iterable<T> { 025 026 protected static final int DEFAULT_SIZE = 7; 027 private static final float LOAD = 3; 028 protected AbstractBucket<T>[] buckets; 029 protected int numElems = 0; 030 031 abstract static class AbstractBucket<T> { 032 /** 033 * Change the next bucket after this bucket, possibly constructing a new 034 * abstract bucket. 035 * 036 * @param next the new value for the next bucket 037 * @return previous bucket of the given bucket 038 */ 039 abstract AbstractBucket<T> setNext(AbstractBucket<T> next); 040 041 abstract AbstractBucket<T> getNext(); 042 043 abstract T getKey(); 044 } 045 046 abstract AbstractBucket<T> createNewBucket(T key, AbstractBucket<T> next); 047 048 @SuppressWarnings("unchecked") 049 protected AbstractBucket<T>[] newBucketArray(int size) { 050 return new AbstractBucket[size]; 051 } 052 053 AbstractHashSetRVM(int size) { 054 buckets = newBucketArray(size); 055 } 056 057 public int size() { 058 return numElems; 059 } 060 061 /** 062 * Advise against growing the buckets if they are immortal, as it will lead 063 * to multiple sets of buckets that will be scanned 064 * 065 * @return whether the map is allowed to grow 066 */ 067 private boolean growMapAllowed() { 068 return !VM.runningVM || !MemoryManager.isImmortal(buckets); 069 } 070 071 public void add(T key) { 072 if (VM.VerifyAssertions) VM._assert(key != null); 073 if (growMapAllowed() && numElems > (buckets.length * LOAD)) { 074 growMap(); 075 } 076 077 int bucketIdx = bucketIndex(key, buckets.length); 078 AbstractBucket<T> cur = buckets[bucketIdx]; 079 while (cur != null && !cur.getKey().equals(key)) { 080 cur = cur.getNext(); 081 } 082 if (cur == null) { 083 buckets[bucketIdx] = createNewBucket(key, buckets[bucketIdx]); 084 numElems++; 085 } 086 } 087 088 public T get(T key) { 089 if (key == null) { 090 return null; 091 } 092 int bucketIdx = bucketIndex(key, buckets.length); 093 AbstractBucket<T> cur = buckets[bucketIdx]; 094 while (cur != null && !cur.getKey().equals(key)) { 095 cur = cur.getNext(); 096 } 097 if (cur == null) { 098 return null; 099 } else { 100 return cur.getKey(); 101 } 102 } 103 104 public boolean contains(T key) { 105 return get(key) != null; 106 } 107 108 public void addAll(AbstractHashSetRVM<T> c) { 109 for (T t : c) { 110 add(t); 111 } 112 } 113 114 private void growMap() { 115 AbstractBucket<T>[] newBuckets = newBucketArray(buckets.length * 2 + 1); 116 for (AbstractBucket<T> cur : buckets) { 117 while (cur != null) { 118 AbstractBucket<T> next = cur.getNext(); 119 int newIdx = bucketIndex(cur.getKey(), newBuckets.length); 120 cur = cur.setNext(newBuckets[newIdx]); 121 newBuckets[newIdx] = cur; 122 cur = next; 123 } 124 } 125 buckets = newBuckets; 126 } 127 128 public void remove(T key) { 129 if (VM.VerifyAssertions) VM._assert(key != null); 130 int bucketIdx = bucketIndex(key, buckets.length); 131 AbstractBucket<T> cur = buckets[bucketIdx]; 132 AbstractBucket<T> prev = null; 133 while (cur != null && !cur.getKey().equals(key)) { 134 prev = cur; 135 cur = cur.getNext(); 136 } 137 if (cur != null) { 138 if (prev == null) { 139 // removing first bucket in chain. 140 buckets[bucketIdx] = cur.getNext(); 141 } else { 142 AbstractBucket<T> newPrev = prev.setNext(cur.getNext()); 143 if (newPrev != prev) { 144 throw new UnsupportedOperationException(); 145 } 146 } 147 numElems--; 148 } 149 } 150 151 public void removeAll() { 152 for (int i = 0; i < buckets.length; i++) { 153 buckets[i] = null; 154 } 155 numElems = 0; 156 } 157 158 @Override 159 public Iterator<T> iterator() { 160 return new SetIterator(); 161 } 162 163 private int bucketIndex(T key, int divisor) { 164 if (key == null) { 165 return 0; 166 } else { 167 return (key.hashCode() & 0x7fffffff) % divisor; 168 } 169 } 170 171 /** 172 * Iterator 173 */ 174 class SetIterator implements Iterator<T> { 175 private int bucketIndex = 0; 176 private AbstractBucket<T> next = null; 177 private int numVisited = 0; 178 179 @Override 180 public T next() { 181 if (!hasNext()) { 182 throw new NoSuchElementException(); 183 } 184 185 while (next == null) { 186 next = buckets[bucketIndex++]; 187 } 188 AbstractBucket<T> ans = next; 189 next = ans.getNext(); 190 numVisited++; 191 return ans.getKey(); 192 } 193 194 @Override 195 public boolean hasNext() { 196 return numVisited < numElems; 197 } 198 199 @Override 200 public void remove() { 201 throw new UnsupportedOperationException(); 202 } 203 } 204}