views:

3978

answers:

8

Hey all,

I know about SortedSet, but in my case I need something that implements List, and not Set. So is there an implementation out there, in the API or elsewhere?

It shouldn't be hard to implement myself, but I figured why not ask people here first?

thanks,

Yuval =8-)

A: 

Why not encapsulate a set with a list, sort like:

new ArrayList( new HashSet() )

This leaves the other implementation for someone who is a real master of Collections ;-)

dhiller
This constructor copies the contents of the Set into the new List, rather than wrapping it.
Calum
@Calum, that is correct, but instead of worrying about not adding duplicates to a List, he can add his objects to a Set (and let the Set worry about filtering out duplicates) and just wrap that Set in a List when passing it to the external method.
matt b
+1  A: 

The documentation (http://java.sun.com/docs/books/tutorial/collections/interfaces/index.html) says: "Set — a collection that cannot contain duplicate elements" "List — an ordered collection (sometimes called a sequence). Lists can contain duplicate elements."

So if you don't want duplicates, you probably shouldn't use a list.

Hauch
I specifically mentioned that I need a List implementation. Trust me, there's a reason.
Yuval
Is the reason because you're interacting with an API that is taking a List as a parameter (instead of a Collection)? That's a bit annoying to have to deal with
matt b
Actually the API takes a Map<AccountType, Map<AccountType, List<Account>>>, which means holding somewhere in the vicinity of dozens to hundreds of lists... bah.
Yuval
+14  A: 

There's no Java collection in the standard library to do this. LinkedHashSet preserves ordering similarly to a List, though, so if you wrap your set in a List when you want to use it as a List you'll get the semantics you want.

Alternatively, the Commons Collections (or collections15, for the generic version) has a List which does what you want already: SetUniqueList.

Calum
The Commons class is exactly what I need, but my boss told me to implement it myself eventually. 10x anyway!
Yuval
Ah well, nothing like reinventing the wheel! You'll know now if the need comes up again, anyway. collections15 is a pretty useful thing to have kicking around; MultiMaps in particular ease the pain of something one ends up implementing oneself a lot.
Calum
@Yuval: your boss is clearly an idiot. I suggest deliberately putting subtle bugs in your implementation to teach him a lesson.
skaffman
@skaffman: he's not actually an idiot, but sometimes he makes moves that are... well, odd. Anyway, I'm not gonna introduce bugs into the product. In today's market, I'm happy with my job and not looking to slam doors and burn bridges, if you get my point.
Yuval
A: 

Off the top of my head, lists allow duplicates. You could quickly implement a UniqueArrayList and override all the add / insert functions to check for 'contains()' before you call the inherited methods. For personal use, you could only implement the 'add' method you use, and override the others to throw an exception in case future programmers try to use the list in a different manner.

Kieveli
I was ready to fall back to this idea (which eventually I had to) if no one suggested anything better =8-) See my own answer above.
Yuval
+1  A: 

So here's what I did eventually. I hope this helps someone else.

class NoDuplicatesList<E> extends LinkedList<E> {
 @Override
 public boolean add(E e) {
  if (this.contains(e)) {
   return false;
  }
  else {
   return super.add(e);
  }
 }

 @Override
 public boolean addAll(Collection<? extends E> collection) {
  Collection<E> copy = new LinkedList<E>(collection);
  copy.removeAll(this);
  return super.addAll(copy);
 }

 @Override
 public boolean addAll(int index, Collection<? extends E> collection) {
  Collection<E> copy = new LinkedList<E>(collection);
  copy.removeAll(this);
  return super.addAll(index, copy);
 }

 @Override
 public void add(int index, E element) {
  if (this.contains(element)) {
   return;
  }
  else {
   super.add(index, element);
  }
 }
}

Yuval =8-)

Yuval
Be careful - LinkedList.contains() needs to scan the entire list to determine if an object is contained in the List. This means that when you are adding objects to a large List, the entire List is scanned for each add operation (in the worst case). This can end up being SLOW.
matt b
Also, your addAll override doesn't check for duplicates in the collection being passed to addAll().
matt b
A: 

You should seriously consider dhiller's answer:

  1. Instead of worrying about adding your objects to a duplicate-less List, add them to a Set (any implementation), which will by nature filter out the duplicates.
  2. When you need to call the method that requires a List, wrap it in a new ArrayList(set) (or a new LinkedList(set), whatever).

I think that the solution you posted with the NoDuplicatesList has some issues, mostly with the contains() method, plus your class does not handle checking for duplicates in the Collection passed to your addAll() method.

matt b
I'd love to learn of these contains() issues. As for the addAll(), I create a copy of the given collection and remove all objects already in 'this'. How does that not handle duplicates?
Yuval
As I mentioned in my comment to your class posting, contains() has to scan the entire list (in the worst case) to find if the object is contained in the list. If you have a list of 1 million items and add 10 it it individually, then (in the worst case) over ten million items are scanned.
matt b
As for addAll(), if the Collection passed to addAll contains duplicates itself, they are not detected. For example: your list {A, B, C, D} parameter list {B, D, E, E, E}. You create a copy of the parameter, and after removeAll it contains {E, E, E}.
matt b
The addAll() issue is not really relevant to me, as I use NoDuplicatesList throughout the procedure, and addAll() should be receiving another NoDuplicatesList as its parameter. What would you suggest to improve the contains() performance?
Yuval
A: 

Look google collections

damian
A: 

Hi all, I needed something like that, so I went to the commons collections and used the SetUniqueList, but when I ran some performance test, I found that it seems not optimized comparing to the case if I want to use a Set and obtain an Array using the Set.toArray() method, the SetUniqueTest took 20:1 time to fill and then traverse 100,000 Strings comparing to the other implementaion, which is a big deal difference, so If you worry about the performance, I recommend you to use the Set and get a Array instead of using the SetUniqueList, unless you really need the logic of the SetUniqueList, then you need to check other solutions...

The testing code main method:

public static void main(String[] args) {

SetUniqueList pq = SetUniqueList.decorate(new ArrayList());
Set s = new TreeSet();

long t1 = 0L;
long t2 = 0L;
String t;


t1 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    pq.add("a" + Math.random());
}
while (!pq.isEmpty()) {
    t = (String) pq.remove(0);
}
t1 = System.nanoTime() - t1;

t2 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    s.add("a" + Math.random());
}

s.clear();
String[] d = (String[]) s.toArray(new String[0]);
s.clear();
for (int i = 0; i < d.length; i++) {
    t = d[i];

}
t2 = System.nanoTime() - t2;

System.out.println((double)t1/1000/1000/1000); //seconds
System.out.println((double)t2/1000/1000/1000); //seconds
System.out.println(((double) t1) / t2);        //comparing results

}

Regards Mohammed Sleem http://abusleem.net/blog