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 }