001 /* 002 * Copyright 2010-2016 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.utils; 018 019 import org.jetbrains.annotations.NotNull; 020 021 import java.lang.reflect.Array; 022 import java.util.*; 023 024 /** 025 * A List which is optimised for the sizes of 0 and 1, 026 * in which cases it would not allocate array at all. 027 * 028 * This class was copied from com.intellij.util.SmartList. 029 */ 030 @SuppressWarnings("unchecked") 031 public class SmartList<E> extends AbstractList<E> implements RandomAccess { 032 private int mySize; 033 private Object myElem; // null if mySize==0, (E)elem if mySize==1, Object[] if mySize>=2 034 035 public SmartList() { } 036 037 public SmartList(E element) { 038 add(element); 039 } 040 041 public SmartList(@NotNull Collection<? extends E> elements) { 042 int size = elements.size(); 043 if (size == 1) { 044 E element = elements instanceof List ? (E)((List)elements).get(0) : elements.iterator().next(); 045 add(element); 046 } 047 else if (size > 0) { 048 mySize = size; 049 myElem = elements.toArray(new Object[size]); 050 } 051 } 052 053 public SmartList(@NotNull E... elements) { 054 if (elements.length == 1) { 055 add(elements[0]); 056 } 057 else if (elements.length > 0) { 058 mySize = elements.length; 059 myElem = Arrays.copyOf(elements, mySize); 060 } 061 } 062 063 @Override 064 public E get(int index) { 065 if (index < 0 || index >= mySize) { 066 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize); 067 } 068 if (mySize == 1) { 069 return (E)myElem; 070 } 071 return (E)((Object[])myElem)[index]; 072 } 073 074 @Override 075 public boolean add(E e) { 076 if (mySize == 0) { 077 myElem = e; 078 } 079 else if (mySize == 1) { 080 Object[] array = new Object[2]; 081 array[0] = myElem; 082 array[1] = e; 083 myElem = array; 084 } 085 else { 086 Object[] array = (Object[])myElem; 087 int oldCapacity = array.length; 088 if (mySize >= oldCapacity) { 089 // have to resize 090 int newCapacity = oldCapacity * 3 / 2 + 1; 091 int minCapacity = mySize + 1; 092 if (newCapacity < minCapacity) { 093 newCapacity = minCapacity; 094 } 095 Object[] oldArray = array; 096 myElem = array = new Object[newCapacity]; 097 System.arraycopy(oldArray, 0, array, 0, oldCapacity); 098 } 099 array[mySize] = e; 100 } 101 102 mySize++; 103 modCount++; 104 return true; 105 } 106 107 @Override 108 public void add(int index, E e) { 109 if (index < 0 || index > mySize) { 110 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize); 111 } 112 113 if (mySize == 0) { 114 myElem = e; 115 } 116 else if (mySize == 1 && index == 0) { 117 Object[] array = new Object[2]; 118 array[0] = e; 119 array[1] = myElem; 120 myElem = array; 121 } 122 else { 123 Object[] array = new Object[mySize + 1]; 124 if (mySize == 1) { 125 array[0] = myElem; // index == 1 126 } 127 else { 128 Object[] oldArray = (Object[])myElem; 129 System.arraycopy(oldArray, 0, array, 0, index); 130 System.arraycopy(oldArray, index, array, index + 1, mySize - index); 131 } 132 array[index] = e; 133 myElem = array; 134 } 135 136 mySize++; 137 modCount++; 138 } 139 140 @Override 141 public int size() { 142 return mySize; 143 } 144 145 @Override 146 public void clear() { 147 myElem = null; 148 mySize = 0; 149 modCount++; 150 } 151 152 @Override 153 public E set(int index, E element) { 154 if (index < 0 || index >= mySize) { 155 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize); 156 } 157 158 E oldValue; 159 if (mySize == 1) { 160 oldValue = (E)myElem; 161 myElem = element; 162 } 163 else { 164 Object[] array = (Object[])myElem; 165 oldValue = (E)array[index]; 166 array[index] = element; 167 } 168 return oldValue; 169 } 170 171 @Override 172 public E remove(int index) { 173 if (index < 0 || index >= mySize) { 174 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize); 175 } 176 177 E oldValue; 178 if (mySize == 1) { 179 oldValue = (E)myElem; 180 myElem = null; 181 } 182 else { 183 Object[] array = (Object[])myElem; 184 oldValue = (E)array[index]; 185 186 if (mySize == 2) { 187 myElem = array[1 - index]; 188 } 189 else { 190 int numMoved = mySize - index - 1; 191 if (numMoved > 0) { 192 System.arraycopy(array, index + 1, array, index, numMoved); 193 } 194 array[mySize - 1] = null; 195 } 196 } 197 mySize--; 198 modCount++; 199 return oldValue; 200 } 201 202 private static class EmptyIterator<T> implements Iterator<T> { 203 private static final EmptyIterator INSTANCE = new EmptyIterator(); 204 public static <T> EmptyIterator<T> getInstance() { 205 //noinspection unchecked 206 return INSTANCE; 207 } 208 @Override 209 public boolean hasNext() { 210 return false; 211 } 212 213 @Override 214 public T next() { 215 throw new NoSuchElementException(); 216 } 217 218 @Override 219 public void remove() { 220 throw new IllegalStateException(); 221 } 222 } 223 224 225 @NotNull 226 @Override 227 public Iterator<E> iterator() { 228 if (mySize == 0) { 229 return EmptyIterator.getInstance(); 230 } 231 if (mySize == 1) { 232 return new SingletonIterator(); 233 } 234 return super.iterator(); 235 } 236 237 238 private abstract static class SingletonIteratorBase<T> implements Iterator<T> { 239 private boolean myVisited; 240 241 @Override 242 public final boolean hasNext() { 243 return !myVisited; 244 } 245 246 @Override 247 public final T next() { 248 if (myVisited) { 249 throw new NoSuchElementException(); 250 } 251 myVisited = true; 252 checkCoModification(); 253 return getElement(); 254 } 255 256 protected abstract void checkCoModification(); 257 258 protected abstract T getElement(); 259 } 260 261 private class SingletonIterator extends SingletonIteratorBase<E> { 262 private final int myInitialModCount; 263 264 public SingletonIterator() { 265 myInitialModCount = modCount; 266 } 267 268 @Override 269 protected E getElement() { 270 return (E)myElem; 271 } 272 273 @Override 274 protected void checkCoModification() { 275 if (modCount != myInitialModCount) { 276 throw new ConcurrentModificationException("ModCount: " + modCount + "; expected: " + myInitialModCount); 277 } 278 } 279 280 @Override 281 public void remove() { 282 checkCoModification(); 283 clear(); 284 } 285 } 286 287 public void sort(Comparator<? super E> comparator) { 288 if (mySize >= 2) { 289 Arrays.sort((E[])myElem, 0, mySize, comparator); 290 } 291 } 292 293 public int getModificationCount() { 294 return modCount; 295 } 296 297 @NotNull 298 @Override 299 public <T> T[] toArray(@NotNull T[] a) { 300 int aLength = a.length; 301 if (mySize == 1) { 302 if (aLength != 0) { 303 a[0] = (T)myElem; 304 } 305 else { 306 T[] r = (T[])Array.newInstance(a.getClass().getComponentType(), 1); 307 r[0] = (T)myElem; 308 return r; 309 } 310 } 311 else if (aLength < mySize) { 312 return (T[])Arrays.copyOf((E[])myElem, mySize, a.getClass()); 313 } 314 else if (mySize != 0) { 315 //noinspection SuspiciousSystemArraycopy 316 System.arraycopy(myElem, 0, a, 0, mySize); 317 } 318 319 if (aLength > mySize) { 320 a[mySize] = null; 321 } 322 return a; 323 } 324 325 /** 326 * Trims the capacity of this list to be the 327 * list's current size. An application can use this operation to minimize 328 * the storage of a list instance. 329 */ 330 public void trimToSize() { 331 if (mySize < 2) return; 332 Object[] array = (Object[])myElem; 333 int oldCapacity = array.length; 334 if (mySize < oldCapacity) { 335 modCount++; 336 myElem = Arrays.copyOf(array, mySize); 337 } 338 } 339 }