I want to compare an array of comparables. The simplest way seems the following (details not shown):
public class ArrayComparable implements Comparable<ArrayComparable>{
ArrayList<Comparable<?>> list = new ArrayList<Comparable<?>>();
#Override
public int compareTo(ArrayComparable ac) {
Iterator<Comparable<?>> itr = ac.list.iterator();
for(Comparable<?> l : list) {
Comparable<?> itrNext = itr.next();
if(itrNext.getClass() == l.getClass()) {
if(itrNext.compareTo(l)) {
//something
} else {
//other stuff
}
} else {
//some other thing
}
}
}
Of course the problem here is that the compareTo as in itrNext.compareTo(l) will not work giving the error: The method compareTo(capture#6-of ?) in the type Comparable<capture#6-of ?> is not applicable for the arguments (Comparable<capture#7-of ?>)
which I understand why (as far as the method is concerned I might be comparing apples to oranges). On the other hand, I know I am not as I check for the class of things before comparing them.
So is there a way I can make this work? Don't worry about the sanity of comparing arrays of any comparables, as I have a good reason why I want to do that.
EDIT- SO why would I want to do something like this. Say I wanted to have an array of comparables, and I didn't care what was contained in each index, as long as the types corresponded, and they could be compared. Then I could do a general lexicographical compare between these arrays. This way I don't have to write a comparable for (int,int) and (int, string), and (string, double, string) or whatever you need. I just write one, and as long as I make sure that the types match (and I can), I am good to go.
Using the raw type Comparable wherever you're currently using Comparable<?> should work. Actually, you could just do that in one place if you want:
if (((Comparable) itrNext).compareTo(l) == 0)
Make ArrayComparable a generic class so that you can properly parameterize the generics rather than using <?> everywhere. Oh, and you might as well implement Iterable as well.
public class ArrayComparable<T> implements Comparable<ArrayComparable<T>>, Iterable<T>
{
List<Comparable<T>> list = new ArrayList<Comparable<T>>();
#Override
public int compareTo(ArrayComparable<T> ac)
{
// snip
}
#Override
public Iterator<T> iterator()
{
return list.iterator();
}
}
Try this:
if(itrNext.getClass().cast(itrNext).compareTo(l.getClass().cast(l))) {
//something
} else {
//other stuff
}
public class GenericDemo<T>{
T g;
public <T extends Comparable<T>> void printData(T a[]){
T max = a[0];
if(a[1].compareTo(max)>0){
max=a[1];
}
if(a[2].compareTo(max)>0){
max=a[1];
}
System.out.println(max);
System.out.println("DataType: " +a.getClass().getName());
}
public static void main(String[] ar)
{
Integer a[]={1,2,3};
Byte b[]= {4,6,7};
Short c[]={6,8,9};
GenericDemo g = new GenericDemo();
g.printData(a);
g.printData(b);
g.printData(c);
}
}
A good answer to this would be:
public final class ArrayComparable<T extends Comparable<T>>
implements Comparable<ArrayComparable<T>> {
private final ArrayList<T> list = new ArrayList<>();
#Override
public int compareTo(final ArrayComparable<T> other) {
final Iterator<T> it = other.list.iterator();
for (final T element : list) {
final T otherElement = it.next();
final int comparison = element.compareTo(otherElement);
if (comparison < 0) {
// something
} else if (comparison > 0) {
// other stuff
} else {
// other stuff
}
}
return 0;
}
}
Related
I am trying to sort my list of objects like this:
List<UsersDataFoundTo> mergedUsers = mergeUsersFound(ldapUsers, foundUsers);
return mergedUsers.sort((UsersDataFoundTo h1, UsersDataFoundTo h2) -> h1.getLastName().compareTo(h2.getLastName()));
and on the return statement I get an error:
Incompatible types.
Required: java.util.List<UsersDataFoundTo>
Found:void
What do I do wrong then?
Much easier would be to write is as:
mergedUsers.sort(Comparator.comparing(UsersDataFoundTo::getLastName))
And sort has a void return type, so basically do a :
return mergedUsers;
For reusable, I think the class UsersDataFoundTo should implements Comparable and override compareTo function.
class UsersDataFoundTo implements Comparable<UsersDataFoundTo> {
private String lastNam;
public String getLastNam() {
return lastNam;
}
public void setLastNam(String lastNam) {
this.lastNam = lastNam;
}
#Override
public int compareTo(UsersDataFoundTo other) {
return getLastNam().compareTo(other.getLastNam());
}
}
Then you can use a collection utility to sort it like this:
List<UsersDataFoundTo> mergedUsers = //...
java.util.Collections.sort(mergedUsers);
I hope this help.
(I was astonished not to be able to find this question already on stackoverflow, which I can only put down to poor googling on my part, by all means point out the duplicate...)
Here is a toy class that returns the reverse of what you put into it. Currently it works on integers, but would require only very minor changes to work for String.
public class Mirror {
int value;
public int get() {
return reverse(value);
}
private int reverse(int value2) {
String valueString = value + "";
String newString = reverse(valueString);
return Integer.parseInt(newString);
}
private String reverse(String valueString) {
String newString = "";
for (char c : valueString.toCharArray()) {
newString = c + newString;
}
return newString;
}
public void set(int value) {
this.value = value;
}
}
What I'd like to do is make the class generic, but only for, say, two or three possible types. So what I want to write is:
public class Mirror<X, where X is one of Integer, String, or MagicValue {
X value
public X get(){
[...]
What's the correct syntax? My Google-fu is failing me... :(
EDIT: it appears there isn't a correct syntax and it can't appear to be done, so my main question is: why? this seems like the sort of thing that people might want to do before they made the class truly generic...
EDIT EDIT: Managed to work out the why with some labmates today, so added the relevant why answer below.
Unfortunately java does not provide such functionality directly. However I can suggest you the following work around:
Create parametrized class Mirror with private constructor and 3 static factory methods that create instance of Mirror with specific parameter:
public class Mirror<T> {
private T value
private Mirror(T value) {
this.value = value;
}
public static Mirror<Integer> integerMirror(Integer value) {
return new Mirror(value);
}
public static Mirror<String> stringMirror(String value) {
return new Mirror(value);
}
public static Mirror<MagicValue> magicMirror(MagicValue value) {
return new Mirror(value);
}
}
EDIT
Obviously you can (and probably should) separate the class Mirror from its creating, e.g. put the factory methods to separate class MirrorFactory. In this case the constructor should become package protected.
If you want to support large yet limited number of classes you can implement only one generic factory method
public static <T> Mirror<T> createMirror(T value) {
checkTypeSupported(value);
return new Mirror(value);
}
Method checkTypeSupported(value); may use some kind of metadatat (e.g. properties, JSON etc file) to get supported types. In this case however you will not enjoy the compile time validation.
Other solution is to require that all supported types extend certain base class or implement interface:
public class Mirror<T extends MyInterface> {}
But this solution seems does not match your requirements since you need Integer, String and MagicValue.
Various ways to do what you need...Here is another option. No getter or setter.
One instance of Mirror for each type to be handled. One reverse() method.
Tweak as necessary. Add error checking/handling.
public class Mirror<T> {
public T reverse(final T value) {
T result = null;
while (true) {
if (value instanceof String) {
System.out.println("Do for String");
result = value;
break;
}
if (value instanceof Integer) {
System.out.println("Do for Integer");
result = value;
break;
}
if (value instanceof JFrame) {
System.out.println("Do for JFrame");
result = value;
break;
}
throw new RuntimeException("ProgramCheck: Missing handler for type " + value.getClass().getSimpleName());
}
return result;
}
Tester:
final Mirror<String> testerString = new Mirror<>();
testerString.reverse("string");
final Mirror<Integer> testerInteger = new Mirror<>();
testerInteger.reverse(41);
testerInteger.reverse(42);
testerInteger.reverse(43);
final Mirror<JFrame> testerJFrame = new Mirror<>();
testerJFrame.reverse(new JFrame());
Results:
Do for String
Do for Integer
Do for Integer
Do for Integer
Do for JFrame
An alternative would be to just accept the fact that you have no control over the type hierarchy of String/Integer and create an interface to give a common type for the classes you do have control over
public int reverse(int value) {
return Integer.valueOf(new StringBuilder(value + "").reverse()
.toString());
}
public String reverse(String value) {
return new StringBuilder(value + "").reverse().toString();
}
public <T extends Reversible> T reverse(T value) {
value.reverse();
return value;
}
public interface Reversible {
public void reverse();
}
And if you only want one instance of the Mirror class...use a generic method.
public class Mirror {
public <T> T reverse(final T value) {
T result = null;
while (true) {
if (value instanceof String) {
System.out.println("Do for String");
result = value;
break;
}
if (value instanceof Integer) {
System.out.println("Do for Integer");
result = value;
break;
}
if (value instanceof JFrame) {
System.out.println("Do for JFrame");
result = value;
break;
}
throw new RuntimeException("ProgramCheck: Missing handler for type " + value.getClass().getSimpleName());
}
return result;
}
tester:
final Mirror tester = new Mirror();
String s = tester.reverse("string");
Integer i41 = tester.reverse(41);
Integer i42 = tester.reverse(42);
Integer i43 = tester.reverse(43);
JFrame j = tester.reverse(new JFrame());
results:
Do for String
Do for Integer
Do for Integer
Do for Integer
Do for JFrame
You can't bound a generic parameter to range of values. You could however restrict it programatically:
public abstract class AbstractMirror<T> {
T value;
protected AbstractMirror(Class<T> clazz) {
if (clazz != Integer.class && clazz != String.class && clazz != MagicValue.class)
throw new IllegalArgumentException();
}
public abstract T get();
protected abstract T reverse(T value);
}
You can use so-called "witness" types to make the compiler do what you want.
public interface Reversible< T > {
public static final class IntReversible implements Reversible< Integer > {}
public static final class StringReversible implements Reversible< String > {}
public static final class MagicReversible implements Reversible< MagicValue > {}
}
public abstract class Mirror< T, R extends Reversible< T > > {
// ...
}
public class IntMirror extends Mirror< Integer, IntReversible > {
// ...
}
However, the reason your example doesn't make any sense is because you gain virtually nothing from using a generic in this context. What possible algorithm will reverse an integer or a string or a MagicValue without resorting to awful run-time type-checking and casting? The code will be all three reverse algorithms, wrapped with a hideous if-ladder.
So here is the why (worked it out at work)
Generics are always from a subclass, although it looks like
Public class Thing<T> {}
will allow any type in there, really what it's saying is that it will allow any subtype of Object. I.e.
Public class Thing<T extends Object> {}
This is effectively working as inheritance, and indeed, the Oracle Website shows us this happening when the syntactic sugar is removed:
In the following example, the generic Node class uses a bounded type
parameter:
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
The Java compiler replaces the bounded type parameter T with the first
bound class, Comparable:
public class Node {
private Comparable data;
private Node next;
public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
public Comparable getData() { return data; }
// ...
}
...and so the answer turns out that the reason you can't limit the types in this way is because it effectively turns into multiple Inheritance, which is nasty, and which I'm happy to avoid....
I am relatively inexperienced with java & generics, so please excuse me if this is a stupid question.
I have 3 very similar helper methods called verifyTextualSort, verifyNumericSort and verifyDateSort.
The 3 methods follow the same pattern with only a slight difference in them:
private boolean verifyTextualSort(...) {
ArrayList<String> list = new ArrayList<String>();
// Do common stuff with the list
// Do textual-specific stuff
// Do common stuff with the list
}
private boolean verifyNumericSort(...) {
ArrayList<Integer> list = new ArrayList<Integer>();
// Do common stuff with the list
// Do Numeric-specific stuff
// Do common stuff with the list
}
Is there some way I can combine them into one method, passing somehow the type (Integer, String, Date) as a parameter? I have to be able to know which is the type from inside the method so that I can do the correct specific stuff.
You need three method for the specific stuff. However for the common stuff you can create a common method they both call.
private boolean verifyNumericSort(...) {
List<Integer> list = new ArrayList<Integer>();
commonStuff1(list);
// Do Numeric-specific stuff
commonStuff2(list);
}
You could pass a Class as a parameter, if that is what you want (as you said, passing the type as a parameter):
public <T> void test(List<T> l, T t, Class<T> c) {
System.out.println(c.getName());
System.out.println(l.get(0).getClass().getName());
System.out.println(t.getClass().getName());
}
All the sysouts above will print out the name of the class, so you'll be able to choose which one suits you the best.
You can't do that by introspection using the Generics because of type erasure. But if the list is not empty, you can check the type of the first element and then invoke appropriate method.
since you have 3 fields you can do this..
class A
{
private Date date = null;
private Integer int = null;
private String text = null;
//add getters and setters for these fields
}
and now pass this class Object as an arguement to that method
public boolean verify(A a){
a.getDate();
a.getInt()
//etc and do your stuff
}
You need generics and refactoring:
private boolean verifyTextualSort(List<String> strings) {
commonStuffA(strings);
// Do textual-specific stuff
commonStuffB(strings);
return true; // ?
}
private boolean verifyNumericSort(List<Integer> ints) {
commonStuffB(ints);
// Do Numeric-specific stuff
commonStuffB(ints);
return true; // ?
}
private void commonStuffA(List<?> things) { // This method accept a list of anything
// Do common stuff A with the list
}
private void commonStuffB(List<?> things) { // This method accept a list of anything
// Do common stuff B with the list
}
private void someCallingMethod() {
List<String> strings = new ArrayList<String>();
verifyTextualSort(strings);
List<Integer> ints = new ArrayList<Integer>();
verifyTextualSort(ints);
}
I think you could possibly do something similar to this:
public <T extends Object> boolean verify(T t)
{
if(!(t==null))
{
if(t instanceof Date)
{
//Do date verify routine
return true;
}
else if(t instanceof String)
{
//Do String verify routine
return true;
}
else
{
//Do default verify routine which could be Integer
return true;
}
}
return false;
}
NOTE:
This is not tested.
As others have mentioned, you can't do that with generics because of type erasure (see the other answers for a link to type erasure). I believe you can get a reasonable solution (without instanceof) with polymorphism. Here is an example:
public class VerifySort
{
public static void main(String[] args)
{
VerifySort verifySort = new VerifySort();
Date testDate = new Date();
Integer testInteger = 17;
String testString = "Blammy";
verifySort.verify(testString);
verifySort.verify(testInteger);
verifySort.verify(testDate);
}
private boolean verify(Date parameter)
{
SimpleDateFormat dateFormat = new SimpleDateFormat();
System.out.print("Date parameter: ");
System.out.println(dateFormat.format(parameter));
return true;
}
private boolean verify(Integer parameter)
{
System.out.print("Integer parameter: ");
System.out.println(parameter);
return true;
}
private boolean verify(String parameter)
{
System.out.print("String parameter: ");
System.out.println(parameter);
return true;
}
The API for the Java Set interface states:
For example, some implementations prohibit null elements and some have restrictions on the types of their elements
I am looking for a basic Set implementation that does not require ordering (as ArrayList provides for the List interface) and that does not permit null. TreeSet, HashSet, and LinkedHashSet all allow null elements. Additionally, TreeSet has the requirement that elements implement Comparable.
It seems that no such basic Set exists currently. Does anyone know why? Or if one does exist where I can find it?
[Edit]: I do not want to allow nulls, because later in the code my class will iterate over all elements in the collection and call a specific method. (I'm actually using HashSet<MyRandomObject>). I would rather fail fast than fail later or accidentally incur some bizarre behavior due to a null being in the set.
Better than extending a particular implementation, you can easily write a proxy implementation of Set that checks for nulls. This analogous to Collections.checkedSet. Other than being applicable to any implementation, you can also be sure that you have overridden all applicable methods. Many flaws have been found by extending concrete collections which then have additional methods added in later versions.
I would say use composition instead of inheritance... it might be more work but it'll be more stable in the face of any changes that Sun might make to the Collections Framework.
public class NoNullSet<E> implements Set<E>
{
/** The set that is wrapped. */
final private Set<E> wrappedSet = new HashSet<E>();
public boolean add(E e)
{
if (e == null)
throw new IllegalArgumentException("You cannot add null to a NoNullSet");
return wrappedSet.add(e);
}
public boolean addAll(Collection<? extends E> c)
{
for (E e : c) add(e);
}
public void clear()
{ wrappedSet.clear(); }
public boolean contains(Object o)
{ return wrappedSet.contains(o); }
... wrap the rest of them ...
}
Note that this implementation does not depend on addAll calling add (which is an implementation detail and should not be used because it cannot be guaranteed to remain true in all Java releases).
There is no basic proprietary Set implementation that ignores or constrains null! There is EnumSet, but that one is tailors for the containment of enum types.
However, creating your own implementation can be avoided, if you use either Guava or Commons Collections:
1. Guava Solution:
Set noNulls = Constraints.constrainedSet(new HashSet(), Constraints.notNull());
2. Commons Collections:
Set noNulls = new HashSet();
CollectionUtils.addIgnoreNull(noNulls, object);
You could use apache collections and its PredicatedCollection class, and set the predicate to not allow nulls. You will get exceptions if someone sends nulls in.
This is a failry general purpose way of doing it - you provide a Filter implementation that can restrict what gets added in whatevber way you want. Take a look at the source for java.util.Collections for ideas on the wrapping (I think my implementaiton of the FilteredCollection class is correct... but it is not extensivly tested). There is a sample program at the end that shows the usage.
public interface Filter<T>
{
boolean accept(T item);
}
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
public class FilteredCollections
{
private FilteredCollections()
{
}
public static <T> Collection<T> filteredCollection(final Collection<T> c,
final Filter<T> filter)
{
return (new FilteredCollection<T>(c, filter));
}
private static class FilteredCollection<E>
implements Collection<E>,
Serializable
{
private final Collection<E> wrapped;
private final Filter<E> filter;
FilteredCollection(final Collection<E> collection, final Filter<E> f)
{
if(collection == null)
{
throw new IllegalArgumentException("collection cannot be null");
}
if(f == null)
{
throw new IllegalArgumentException("f cannot be null");
}
wrapped = collection;
filter = f;
}
public int size()
{
return (wrapped.size());
}
public boolean isEmpty()
{
return (wrapped.isEmpty());
}
public boolean contains(final Object o)
{
return (wrapped.contains(o));
}
public Iterator<E> iterator()
{
return new Iterator<E>()
{
final Iterator<? extends E> i = wrapped.iterator();
public boolean hasNext()
{
return (i.hasNext());
}
public E next()
{
return (i.next());
}
public void remove()
{
i.remove();
}
};
}
public Object[] toArray()
{
return (wrapped.toArray());
}
public <T> T[] toArray(final T[] a)
{
return (wrapped.toArray(a));
}
public boolean add(final E e)
{
final boolean ret;
if(filter.accept(e))
{
ret = wrapped.add(e);
}
else
{
// you could throw an exception instead if you want -
// IllegalArgumentException is what I would suggest
ret = false;
}
return (ret);
}
public boolean remove(final Object o)
{
return (wrapped.remove(o));
}
public boolean containsAll(final Collection<?> c)
{
return (wrapped.containsAll(c));
}
public boolean addAll(final Collection<? extends E> c)
{
final E[] a;
boolean result;
a = (E[])wrapped.toArray();
result = false;
for(final E e : a)
{
result |= wrapped.add(e);
}
return result;
}
public boolean removeAll(final Collection<?> c)
{
return (wrapped.removeAll(c));
}
public boolean retainAll(final Collection<?> c)
{
return (wrapped.retainAll(c));
}
public void clear()
{
wrapped.clear();
}
public String toString()
{
return (wrapped.toString());
}
}
}
import java.util.ArrayList;
import java.util.Collection;
public class Main
{
private static class NullFilter<T>
implements Filter<T>
{
public boolean accept(final T item)
{
return (item != null);
}
}
public static void main(final String[] argv)
{
final Collection<String> strings;
strings = FilteredCollections.filteredCollection(new ArrayList<String>(),
new NullFilter<String>());
strings.add("hello");
strings.add(null);
strings.add("world");
if(strings.size() != 2)
{
System.err.println("ERROR: strings.size() == " + strings.size());
}
System.out.println(strings);
}
}
Yes -- in the docs for com.google.common.collect.ImmutableSet:
A high-performance, immutable Set with reliable, user-specified iteration order. Does not permit null elements.
You could easily write your own, by subclassing an appropriate existing class, and overriding all relevant methods so that you can't add null elements.
You may also wish to check out Google Collections. They are more null phobic, I believe.
for me, I didn't find one,
so I overrode the add function
Collection<String> errors = new HashSet<String>() {
#Override
public boolean add(String s) {
return StringUtil.hasContent(s) && super.add(s);//we don't want add null and we allow HashSet.add(null)
}
};
[Edit]: I do not want to allow nulls, because later in the code my
class will iterate over all elements in the collection and call a
specific method.
Instead of checking null, every time, we can simply remove the null once before iterating over the set.
You can remove the null values using set.remove(null);
Set<String> set = new HashSet<>();
set.add("test");
set.add(null);
set.add(null);
System.out.println(set);
set.remove(null);
System.out.println(set);
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
Output
[null, test]
[test]
test
BTW, if you'd asked for a Map implementation that does not allow nulls, the old java.util.Hashtable does not.
In this particular question/example surely if you have a HashSet<MyRandomObject> mySet call mySet.remove(null) before starting the iteration over all elements you mentioned?
I am not sure of a type which this is true. But could you not inherit from a collection or HashTable of your choice and override the Add method, throwing an exception if the element is null?
Why do you not want to allow null?
Do you want to throw an exception if null is added to your set? If so, just do something like this:
private Set<Object> mySet = new HashSet<Object>() {
#Override
public boolean add(Object e) {
if (e == null)
throw new IllegalArgumentException("null"); // or NPE
// or, of course, you could just return false
return super.add(e);
}
};
HashSet's addAll() calls add() repeatedly, so this is the only method you'd have to override.
Hashtable does not allow null values......
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?
There's no Java collection in the standard library to do this. LinkedHashSet<E> 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 commons-collections4, for the generic version) has a List which does what you want already: SetUniqueList / SetUniqueList<E>.
Here is what I did and it works.
Assuming I have an ArrayList to work with the first thing I did was created a new LinkedHashSet.
LinkedHashSet<E> hashSet = new LinkedHashSet<E>()
Then I attempt to add my new element to the LinkedHashSet. The add method does not alter the LinkedHasSet and returns false if the new element is a duplicate. So this becomes a condition I can test before adding to the ArrayList.
if (hashSet.add(E)) arrayList.add(E);
This is a simple and elegant way to prevent duplicates from being added to an array list. If you want you can encapsulate it in and override of the add method in a class that extends the ArrayList. Just remember to deal with addAll by looping through the elements and calling the add method.
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);
}
}
}
Why not encapsulate a set with a list, sort like:
new ArrayList( new LinkedHashSet() )
This leaves the other implementation for someone who is a real master of Collections ;-)
You should seriously consider dhiller's answer:
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.
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.
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 implementation, which is a big deal difference.
So, if you worry about the performance, I recommend you to use the Set and Get an Array instead of using the SetUniqueList, unless you really need the logic of the SetUniqueList, then you'll need to check other solutions...
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
My lastest implementation: https://github.com/marcolopes/dma/blob/master/org.dma.java/src/org/dma/java/util/UniqueArrayList.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* Extends <tt>ArrayList</tt> and guarantees no duplicate elements
*/
public class UniqueArrayList<T> extends ArrayList<T> {
private static final long serialVersionUID = 1L;
public UniqueArrayList(int initialCapacity) {
super(initialCapacity);
}
public UniqueArrayList() {
super();
}
public UniqueArrayList(T[] array) {
this(Arrays.asList(array));
}
public UniqueArrayList(Collection<? extends T> col) {
addAll(col);
}
#Override
public void add(int index, T e) {
if (!contains(e)) super.add(index, e);
}
#Override
public boolean add(T e) {
return contains(e) ? false : super.add(e);
}
#Override
public boolean addAll(Collection<? extends T> col) {
Collection set=new LinkedHashSet(this);
set.addAll(col);
clear();
return super.addAll(set);
}
#Override
public boolean addAll(int index, Collection<? extends T> col) {
Collection set=new LinkedHashSet(subList(0, index));
set.addAll(col);
set.addAll(subList(index, size()));
clear();
return super.addAll(set);
}
#Override
public T set(int index, T e) {
return contains(e) ? null : super.set(index, e);
}
/** Ensures element.equals(o) */
#Override
public int indexOf(Object o) {
int index=0;
for(T element: this){
if (element.equals(o)) return index;
index++;
}return -1;
}
}
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.
The documentation for collection interfaces 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.
in add method, why not using HashSet.add() to check duplicates instead of HashSet.consist().
HashSet.add() will return true if no duplicate and false otherwise.
What about this?
Just check the list before adding with a contains for an already existing object
while (searchResult != null && searchResult.hasMore()) {
SearchResult nextElement = searchResult.nextElement();
Attributes attributes = nextElement.getAttributes();
String stringName = getAttributeStringValue(attributes, SearchAttribute.*attributeName*);
if(!List.contains(stringName)){
List.add(stringName);
}
}
I just made my own UniqueList in my own little library like this:
package com.bprog.collections;//my own little set of useful utilities and classes
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
/**
*
* #author Jonathan
*/
public class UniqueList {
private HashSet masterSet = new HashSet();
private ArrayList growableUniques;
private Object[] returnable;
public UniqueList() {
growableUniques = new ArrayList();
}
public UniqueList(int size) {
growableUniques = new ArrayList(size);
}
public void add(Object thing) {
if (!masterSet.contains(thing)) {
masterSet.add(thing);
growableUniques.add(thing);
}
}
/**
* Casts to an ArrayList of unique values
* #return
*/
public List getList(){
return growableUniques;
}
public Object get(int index) {
return growableUniques.get(index);
}
public Object[] toObjectArray() {
int size = growableUniques.size();
returnable = new Object[size];
for (int i = 0; i < size; i++) {
returnable[i] = growableUniques.get(i);
}
return returnable;
}
}
I have a TestCollections class that looks like this:
package com.bprog.collections;
import com.bprog.out.Out;
/**
*
* #author Jonathan
*/
public class TestCollections {
public static void main(String[] args){
UniqueList ul = new UniqueList();
ul.add("Test");
ul.add("Test");
ul.add("Not a copy");
ul.add("Test");
//should only contain two things
Object[] content = ul.toObjectArray();
Out.pl("Array Content",content);
}
}
Works fine. All it does is it adds to a set if it does not have it already and there's an Arraylist that is returnable, as well as an object array.