I\'m looking for a good sorted list for java. Googling around give me some hints about using TreeSet/TreeMap. But these components is lack of one thing: random access to an
Generally you can't have constant time look up and log time deletions/insertions, but if you're happy with log time look ups then you can use a SortedList.
Not sure if you'll trust my coding but I recently wrote a SortedList implementation in Java, which you can download from http://www.scottlogic.co.uk/2010/12/sorted_lists_in_java/. This implementation allows you to look up the i-th element of the list in log time.
Phuong:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class test
{
public static void main(String[] args)
{
List<Integer> nums = new ArrayList<Integer>();
Random rand = new Random();
for( int i = 0; i < 40000; i++ )
{
nums.add( rand.nextInt(Integer.MAX_VALUE) );
}
long start = System.nanoTime();
Collections.sort(nums);
long end = System.nanoTime();
System.out.println((end-start)/1e9);
}
}
Since you rarely need sorting, as per your problem statement, this is probably more efficient than it needs to be.
To test the efficiancy of earlier awnser by Konrad Holl, I did a quick comparison with what I thought would be the slow way of doing it:
package util.collections;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
*
* @author Earl Bosch
* @param <E> Comparable Element
*
*/
public class SortedList<E extends Comparable> implements List<E> {
/**
* The list of elements
*/
private final List<E> list = new ArrayList();
public E first() {
return list.get(0);
}
public E last() {
return list.get(list.size() - 1);
}
public E mid() {
return list.get(list.size() >>> 1);
}
@Override
public void clear() {
list.clear();
}
@Override
public boolean add(E e) {
list.add(e);
Collections.sort(list);
return true;
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object obj) {
return list.contains((E) obj);
}
@Override
public Iterator<E> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] arg0) {
return list.toArray(arg0);
}
@Override
public boolean remove(Object obj) {
return list.remove((E) obj);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
list.addAll(c);
Collections.sort(list);
return true;
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
@Override
public E get(int index) {
return list.get(index);
}
@Override
public E set(int index, E element) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void add(int index, E element) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public E remove(int index) {
return list.remove(index);
}
@Override
public int indexOf(Object obj) {
return list.indexOf((E) obj);
}
@Override
public int lastIndexOf(Object obj) {
return list.lastIndexOf((E) obj);
}
@Override
public ListIterator<E> listIterator() {
return list.listIterator();
}
@Override
public ListIterator<E> listIterator(int index) {
return list.listIterator(index);
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException("Not supported.");
}
}
Turns out its about twice as fast! I think its because of SortedLinkList slow get - which make's it not a good choice for a list.
Compared times for same random list:
Seems glazedlists.SortedList is really fast...
You need no sorted list. You need no sorting at all.
I need to add/remove keys from the list when object is added / removed from database.
But not immediately, the removal can wait. Use an ArrayList
containing the ID's all alive objects plus at most some bounded percentage of deleted objects. Use a separate HashSet
to keep track of deleted objects.
private List<ID> mostlyAliveIds = new ArrayList<>();
private Set<ID> deletedIds = new HashSet<>();
I want to randomly select few dozens of element from the whole list.
ID selectOne(Random random) {
checkState(deletedIds.size() < mostlyAliveIds.size());
while (true) {
int index = random.nextInt(mostlyAliveIds.size());
ID id = mostlyAliveIds.get(index);
if (!deletedIds.contains(ID)) return ID;
}
}
Set<ID> selectSome(Random random, int count) {
checkArgument(deletedIds.size() <= mostlyAliveIds.size() - count);
Set<ID> result = new HashSet<>();
while (result.size() < count) result.add(selectOne(random));
}
For maintaining the data, do something like
void insert(ID id) {
if (!deletedIds.remove(id)) mostlyAliveIds.add(ID);
}
void delete(ID id) {
if (!deletedIds.add(id)) {
throw new ImpossibleException("Deleting a deleted element);
}
if (deletedIds.size() > 0.1 * mostlyAliveIds.size()) {
mostlyAliveIds.removeAll(deletedIds);
deletedIds.clear();
}
}
The only tricky part is the insert
which has to check if an already deleted ID was resurrected.
The delete
ensures that no more than 10% of elements in mostlyAliveIds
are deleted IDs. When this happens, they get all removed in one sweep (I didn't check the JDK sources, but I hope, they do it right) and the show goes on.
With no more than 10% of dead IDs, the overhead of selectOne
is no more than 10% on the average.
I'm pretty sure that it's faster than any sorting as the amortized complexity is O(n)
.