001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.hadoop.hdfs.util;
019
020 import java.util.ConcurrentModificationException;
021 import java.util.Iterator;
022 import java.util.ArrayList;
023 import java.util.List;
024 import java.util.NoSuchElementException;
025
026 /**
027 * A low memory linked hash set implementation, which uses an array for storing
028 * the elements and linked lists for collision resolution. In addition it stores
029 * elements in a linked list to ensure ordered traversal. This class does not
030 * support null element.
031 *
032 * This class is not thread safe.
033 *
034 */
035 public class LightWeightLinkedSet<T> extends LightWeightHashSet<T> {
036 /**
037 * Elements of {@link LightWeightLinkedSet}.
038 */
039 static class DoubleLinkedElement<T> extends LinkedElement<T> {
040 // references to elements within all-element linked list
041 private DoubleLinkedElement<T> before;
042 private DoubleLinkedElement<T> after;
043
044 public DoubleLinkedElement(T elem, int hashCode) {
045 super(elem, hashCode);
046 this.before = null;
047 this.after = null;
048 }
049
050 @Override
051 public String toString() {
052 return super.toString();
053 }
054 }
055
056 private DoubleLinkedElement<T> head;
057 private DoubleLinkedElement<T> tail;
058
059 /**
060 * @param initCapacity
061 * Recommended size of the internal array.
062 * @param maxLoadFactor
063 * used to determine when to expand the internal array
064 * @param minLoadFactor
065 * used to determine when to shrink the internal array
066 */
067 public LightWeightLinkedSet(int initCapacity, float maxLoadFactor,
068 float minLoadFactor) {
069 super(initCapacity, maxLoadFactor, minLoadFactor);
070 head = null;
071 tail = null;
072 }
073
074 public LightWeightLinkedSet() {
075 this(MINIMUM_CAPACITY, DEFAULT_MAX_LOAD_FACTOR, DEFAUT_MIN_LOAD_FACTOR);
076 }
077
078 /**
079 * Add given element to the hash table
080 *
081 * @return true if the element was not present in the table, false otherwise
082 */
083 @Override
084 protected boolean addElem(final T element) {
085 // validate element
086 if (element == null) {
087 throw new IllegalArgumentException("Null element is not supported.");
088 }
089 // find hashCode & index
090 final int hashCode = element.hashCode();
091 final int index = getIndex(hashCode);
092 // return false if already present
093 if (getContainedElem(index, element, hashCode) != null) {
094 return false;
095 }
096
097 modification++;
098 size++;
099
100 // update bucket linked list
101 DoubleLinkedElement<T> le = new DoubleLinkedElement<T>(element, hashCode);
102 le.next = entries[index];
103 entries[index] = le;
104
105 // insert to the end of the all-element linked list
106 le.after = null;
107 le.before = tail;
108 if (tail != null) {
109 tail.after = le;
110 }
111 tail = le;
112 if (head == null) {
113 head = le;
114 }
115 return true;
116 }
117
118 /**
119 * Remove the element corresponding to the key, given key.hashCode() == index.
120 *
121 * @return Return the entry with the element if exists. Otherwise return null.
122 */
123 @Override
124 protected DoubleLinkedElement<T> removeElem(final T key) {
125 DoubleLinkedElement<T> found = (DoubleLinkedElement<T>) (super
126 .removeElem(key));
127 if (found == null) {
128 return null;
129 }
130
131 // update linked list
132 if (found.after != null) {
133 found.after.before = found.before;
134 }
135 if (found.before != null) {
136 found.before.after = found.after;
137 }
138 if (head == found) {
139 head = head.after;
140 }
141 if (tail == found) {
142 tail = tail.before;
143 }
144 return found;
145 }
146
147 /**
148 * Remove and return first element on the linked list of all elements.
149 *
150 * @return first element
151 */
152 public T pollFirst() {
153 if (head == null) {
154 return null;
155 }
156 T first = head.element;
157 this.remove(first);
158 return first;
159 }
160
161 /**
162 * Remove and return n elements from the hashtable.
163 * The order in which entries are removed is corresponds
164 * to the order in which they were inserted.
165 *
166 * @return first element
167 */
168 @Override
169 public List<T> pollN(int n) {
170 if (n >= size) {
171 // if we need to remove all elements then do fast polling
172 return pollAll();
173 }
174 List<T> retList = new ArrayList<T>(n);
175 while (n-- > 0 && head != null) {
176 T curr = head.element;
177 this.removeElem(curr);
178 retList.add(curr);
179 }
180 shrinkIfNecessary();
181 return retList;
182 }
183
184 /**
185 * Remove all elements from the set and return them in order. Traverse the
186 * link list, don't worry about hashtable - faster version of the parent
187 * method.
188 */
189 @Override
190 public List<T> pollAll() {
191 List<T> retList = new ArrayList<T>(size);
192 while (head != null) {
193 retList.add(head.element);
194 head = head.after;
195 }
196 this.clear();
197 return retList;
198 }
199
200 @Override
201 @SuppressWarnings("unchecked")
202 public <U> U[] toArray(U[] a) {
203 if (a == null) {
204 throw new NullPointerException("Input array can not be null");
205 }
206 if (a.length < size) {
207 a = (U[]) java.lang.reflect.Array.newInstance(a.getClass()
208 .getComponentType(), size);
209 }
210 int currentIndex = 0;
211 DoubleLinkedElement<T> current = head;
212 while (current != null) {
213 T curr = current.element;
214 a[currentIndex++] = (U) curr;
215 current = current.after;
216 }
217 return a;
218 }
219
220 @Override
221 public Iterator<T> iterator() {
222 return new LinkedSetIterator();
223 }
224
225 private class LinkedSetIterator implements Iterator<T> {
226 /** The starting modification for fail-fast. */
227 private final int startModification = modification;
228 /** The next element to return. */
229 private DoubleLinkedElement<T> next = head;
230
231 @Override
232 public boolean hasNext() {
233 return next != null;
234 }
235
236 @Override
237 public T next() {
238 if (modification != startModification) {
239 throw new ConcurrentModificationException("modification="
240 + modification + " != startModification = " + startModification);
241 }
242 if (next == null) {
243 throw new NoSuchElementException();
244 }
245 final T e = next.element;
246 // find the next element
247 next = next.after;
248 return e;
249 }
250
251 @Override
252 public void remove() {
253 throw new UnsupportedOperationException("Remove is not supported.");
254 }
255 }
256
257 /**
258 * Clear the set. Resize it to the original capacity.
259 */
260 @Override
261 public void clear() {
262 super.clear();
263 this.head = null;
264 this.tail = null;
265 }
266 }