// Copyright 2017 JanusGraph Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.janusgraph.util.datastructures;
import com.google.common.base.Preconditions;
import java.util.*;
/**
* A list that allows efficient random removals.
*
* @param <T>
*
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class RandomRemovalList<T> implements Collection<T>, Iterator<T> {
public static final Random random = new Random();
private List<T> list;
private final static int numTriesBeforeCompactification = 13;
private static final double fillFactor = 1.05;
private int size;
private int numberOfCompactions;
private boolean isIterating;
public RandomRemovalList() {
this(10);
}
public RandomRemovalList(int capacity) {
list = new ArrayList<T>(capacity);
size = 0;
numberOfCompactions = 0;
isIterating = false;
}
public RandomRemovalList(Collection<T> objs) {
list = new ArrayList<T>(objs);
size = objs.size();
numberOfCompactions = 0;
isIterating = false;
}
@Override
public boolean add(T obj) {
Preconditions.checkNotNull(obj, "Random Removal lists only contain non-null elements");
Preconditions.checkArgument(!isIterating, "Cannot add to a random removal list while it is being iterated over");
size++;
return list.add(obj);
}
public T getRandom() {
assert size >= 0;
if (size == 0) throw new NoSuchElementException("List is empty");
int numTries = 0;
T element = null;
int pos = -1;
do {
pos = random.nextInt(list.size());
element = list.get(pos);
numTries++;
} while (element == null && numTries < numTriesBeforeCompactification);
if (element != null) {
list.set(pos, null);
size--;
return element;
} else {
//Compact list
List<T> newlist = new ArrayList<T>((int) Math.ceil(fillFactor * size));
for (T obj : list) {
if (obj != null) newlist.add(obj);
}
list = newlist;
numberOfCompactions++;
return getRandom();
}
}
@Override
public int size() {
assert size >= 0;
return size;
}
@Override
public boolean isEmpty() {
assert size >= 0;
return size == 0;
}
public int getNumCompactions() {
return numberOfCompactions;
}
@Override
public boolean addAll(Collection<? extends T> c) {
boolean ret = true;
for (T obj : c) {
if (!add(obj)) ret = false;
}
return ret;
}
@Override
public void clear() {
list.clear();
size = 0;
numberOfCompactions = 0;
}
@Override
public boolean contains(Object o) {
if (o == null) return false;
return list.contains(o);
}
@Override
public boolean containsAll(Collection<?> c) {
boolean ret = true;
for (Object obj : c) {
if (!contains(obj)) ret = false;
}
return ret;
}
@Override
public boolean hasNext() {
isIterating = true;
return !isEmpty();
}
@Override
public T next() {
isIterating = true;
return getRandom();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Element has already been removed");
}
@Override
public Iterator<T> iterator() {
return this;
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public Object[] toArray() {
throw new UnsupportedOperationException();
}
@Override
public <E> E[] toArray(E[] a) {
throw new UnsupportedOperationException();
}
}