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 */ 018package org.apache.hadoop.util; 019 020import java.util.Collection; 021import java.util.Iterator; 022import java.util.NoSuchElementException; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.apache.hadoop.classification.InterfaceAudience; 027 028import com.google.common.base.Preconditions; 029 030/** 031 * Implements an intrusive doubly-linked list. 032 * 033 * An intrusive linked list is one in which the elements themselves are 034 * responsible for storing the pointers to previous and next elements. 035 * This can save a lot of memory if there are many elements in the list or 036 * many lists. 037 */ 038@InterfaceAudience.Private 039public class IntrusiveCollection<E extends IntrusiveCollection.Element> 040 implements Collection<E> { 041 /** 042 * An element contained in this list. 043 * 044 * We pass the list itself as a parameter so that elements can belong to 045 * multiple lists. (The element will need to store separate prev and next 046 * pointers for each.) 047 */ 048 @InterfaceAudience.Private 049 public interface Element { 050 /** 051 * Insert this element into the list. This is the first thing that will 052 * be called on the element. 053 */ 054 void insertInternal(IntrusiveCollection<? extends Element> list, 055 Element prev, Element next); 056 057 /** 058 * Set the prev pointer of an element already in the list. 059 */ 060 void setPrev(IntrusiveCollection<? extends Element> list, Element prev); 061 062 /** 063 * Set the next pointer of an element already in the list. 064 */ 065 void setNext(IntrusiveCollection<? extends Element> list, Element next); 066 067 /** 068 * Remove an element from the list. This is the last thing that will be 069 * called on an element. 070 */ 071 void removeInternal(IntrusiveCollection<? extends Element> list); 072 073 /** 074 * Get the prev pointer of an element. 075 */ 076 Element getPrev(IntrusiveCollection<? extends Element> list); 077 078 /** 079 * Get the next pointer of an element. 080 */ 081 Element getNext(IntrusiveCollection<? extends Element> list); 082 083 /** 084 * Returns true if this element is in the provided list. 085 */ 086 boolean isInList(IntrusiveCollection<? extends Element> list); 087 } 088 089 private Element root = new Element() { 090 // We keep references to the first and last elements for easy access. 091 Element first = this; 092 Element last = this; 093 094 @Override 095 public void insertInternal(IntrusiveCollection<? extends Element> list, 096 Element prev, Element next) { 097 throw new RuntimeException("Can't insert root element"); 098 } 099 100 @Override 101 public void setPrev(IntrusiveCollection<? extends Element> list, 102 Element prev) { 103 Preconditions.checkState(list == IntrusiveCollection.this); 104 last = prev; 105 } 106 107 @Override 108 public void setNext(IntrusiveCollection<? extends Element> list, 109 Element next) { 110 Preconditions.checkState(list == IntrusiveCollection.this); 111 first = next; 112 } 113 114 @Override 115 public void removeInternal(IntrusiveCollection<? extends Element> list) { 116 throw new RuntimeException("Can't remove root element"); 117 } 118 119 @Override 120 public Element getNext( 121 IntrusiveCollection<? extends Element> list) { 122 Preconditions.checkState(list == IntrusiveCollection.this); 123 return first; 124 } 125 126 @Override 127 public Element getPrev( 128 IntrusiveCollection<? extends Element> list) { 129 Preconditions.checkState(list == IntrusiveCollection.this); 130 return last; 131 } 132 133 @Override 134 public boolean isInList(IntrusiveCollection<? extends Element> list) { 135 return list == IntrusiveCollection.this; 136 } 137 138 @Override 139 public String toString() { 140 return "root"; // + IntrusiveCollection.this + "]"; 141 } 142 }; 143 144 private int size = 0; 145 146 /** 147 * An iterator over the intrusive collection. 148 * 149 * Currently, you can remove elements from the list using 150 * #{IntrusiveIterator#remove()}, but modifying the collection in other 151 * ways during the iteration is not supported. 152 */ 153 public class IntrusiveIterator implements Iterator<E> { 154 Element cur; 155 Element next; 156 157 IntrusiveIterator() { 158 this.cur = root; 159 this.next = null; 160 } 161 162 @Override 163 public boolean hasNext() { 164 if (next == null) { 165 next = cur.getNext(IntrusiveCollection.this); 166 } 167 return next != root; 168 } 169 170 @SuppressWarnings("unchecked") 171 @Override 172 public E next() { 173 if (next == null) { 174 next = cur.getNext(IntrusiveCollection.this); 175 } 176 if (next == root) { 177 throw new NoSuchElementException(); 178 } 179 cur = next; 180 next = null; 181 return (E)cur; 182 } 183 184 @Override 185 public void remove() { 186 if (cur == null) { 187 throw new IllegalStateException("Already called remove " + 188 "once on this element."); 189 } 190 next = removeElement(cur); 191 cur = null; 192 } 193 } 194 195 private Element removeElement(Element elem) { 196 Element prev = elem.getPrev(IntrusiveCollection.this); 197 Element next = elem.getNext(IntrusiveCollection.this); 198 elem.removeInternal(IntrusiveCollection.this); 199 prev.setNext(IntrusiveCollection.this, next); 200 next.setPrev(IntrusiveCollection.this, prev); 201 size--; 202 return next; 203 } 204 205 /** 206 * Get an iterator over the list. This can be used to remove elements. 207 * It is not safe to do concurrent modifications from other threads while 208 * using this iterator. 209 * 210 * @return The iterator. 211 */ 212 public Iterator<E> iterator() { 213 return new IntrusiveIterator(); 214 } 215 216 @Override 217 public int size() { 218 return size; 219 } 220 221 @Override 222 public boolean isEmpty() { 223 return size == 0; 224 } 225 226 @Override 227 public boolean contains(Object o) { 228 try { 229 Element element = (Element)o; 230 return element.isInList(this); 231 } catch (ClassCastException e) { 232 return false; 233 } 234 } 235 236 @Override 237 public Object[] toArray() { 238 Object ret[] = new Object[size]; 239 int i = 0; 240 for (Iterator<E> iter = iterator(); iter.hasNext(); ) { 241 ret[i++] = iter.next(); 242 } 243 return ret; 244 } 245 246 @SuppressWarnings("unchecked") 247 @Override 248 public <T> T[] toArray(T[] array) { 249 if (array.length < size) { 250 return (T[])toArray(); 251 } else { 252 int i = 0; 253 for (Iterator<E> iter = iterator(); iter.hasNext(); ) { 254 array[i++] = (T)iter.next(); 255 } 256 } 257 return array; 258 } 259 260 /** 261 * Add an element to the end of the list. 262 * 263 * @param elem The new element to add. 264 */ 265 @Override 266 public boolean add(E elem) { 267 if (elem == null) { 268 return false; 269 } 270 if (elem.isInList(this)) { 271 return false; 272 } 273 Element prev = root.getPrev(IntrusiveCollection.this); 274 prev.setNext(IntrusiveCollection.this, elem); 275 root.setPrev(IntrusiveCollection.this, elem); 276 elem.insertInternal(IntrusiveCollection.this, prev, root); 277 size++; 278 return true; 279 } 280 281 /** 282 * Add an element to the front of the list. 283 * 284 * @param elem The new element to add. 285 */ 286 public boolean addFirst(Element elem) { 287 if (elem == null) { 288 return false; 289 } 290 if (elem.isInList(this)) { 291 return false; 292 } 293 Element next = root.getNext(IntrusiveCollection.this); 294 next.setPrev(IntrusiveCollection.this, elem); 295 root.setNext(IntrusiveCollection.this, elem); 296 elem.insertInternal(IntrusiveCollection.this, root, next); 297 size++; 298 return true; 299 } 300 301 public static final Log LOG = LogFactory.getLog(IntrusiveCollection.class); 302 303 @Override 304 public boolean remove(Object o) { 305 try { 306 Element elem = (Element)o; 307 if (!elem.isInList(this)) { 308 return false; 309 } 310 removeElement(elem); 311 return true; 312 } catch (ClassCastException e) { 313 return false; 314 } 315 } 316 317 @Override 318 public boolean containsAll(Collection<?> collection) { 319 for (Object o : collection) { 320 if (!contains(o)) { 321 return false; 322 } 323 } 324 return true; 325 } 326 327 @Override 328 public boolean addAll(Collection<? extends E> collection) { 329 boolean changed = false; 330 for (E elem : collection) { 331 if (add(elem)) { 332 changed = true; 333 } 334 } 335 return changed; 336 } 337 338 @Override 339 public boolean removeAll(Collection<?> collection) { 340 boolean changed = false; 341 for (Object elem : collection) { 342 if (remove(elem)) { 343 changed = true; 344 } 345 } 346 return changed; 347 } 348 349 @Override 350 public boolean retainAll(Collection<?> collection) { 351 boolean changed = false; 352 for (Iterator<E> iter = iterator(); 353 iter.hasNext(); ) { 354 Element elem = iter.next(); 355 if (!collection.contains(elem)) { 356 iter.remove(); 357 changed = true; 358 } 359 } 360 return changed; 361 } 362 363 /** 364 * Remove all elements. 365 */ 366 @Override 367 public void clear() { 368 for (Iterator<E> iter = iterator(); iter.hasNext(); ) { 369 iter.next(); 370 iter.remove(); 371 } 372 } 373}