Unique classes in the general list

I have a generic class with a generic list in it. I want the generic list to contain only unique classes.

What I have done so far is to compare class names with reflection (getClass ()). But I think this is not a clean solution. Are there any verification recommendations?

public class MyGenericClass<T extends MyGenericClass.MyInterface> { private List<T> members = new ArrayList<>(0); public void add(T t) { final boolean[] classInMembers = {false}; members.forEach(member -> { if (member.getClass().getName().equals(t.getClass().getName())) { classInMembers[0] = true; } }); if (!classInMembers[0]) { members.add(t); } } public interface MyInterface { void doSomething(String text); } } 
 public class Main { public static void main(String[] args) { MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>(); myGenericClass.add(new Performer1()); myGenericClass.add(new Performer2()); myGenericClass.add(new Performer3()); myGenericClass.add(new Performer3()); // should not be inserted! } private static class Performer1 implements MyGenericClass.MyInterface { @Override public void doSomething(String text) { text = "Hi, I am performer 1!"; } } private static class Performer2 implements MyGenericClass.MyInterface { @Override public void doSomething(String text) { text = "Hi, I am performer 2!"; } } private static class Performer3 implements MyGenericClass.MyInterface { @Override public void doSomething(String text) { text = "Hi, I am performer 3!"; } } } 
+4
source share
6 answers

You can subclass the implementation of java.util.Set . Most likely, it will be easier for a subclass of java.util.AbstractSet .

By default, "Set" will compare objects using the .equals() method. In your case, this is not enough. You need to override the contains method to make sure that only instances of a unique class are added.

In your override, contains is probably the same / easier to compare class instances, not their package string name

those. use a.getClass() == b.getClass() , not a.getClass().getName()

+2
source

Do not use List, instead of java.util.Set .

A collection that does not contain duplicate elements. More formally, the sets do not contain a pair of elements e1 and e2 such that e1.equals (e2) and no more than one zero element.

If the iteration order is important or you want to use a custom Comparator , you can use TreeSet :

TreeMap based NavigableSet implementation. Elements are ordered using their natural ordering or with the help of a comparator provided at a given creation time, depending on which constructor is used.

An example of a Set using Comparator :

 class MyComparator implements Comparator<Object> { @Override public int compare(Object e1, Object e2) { if (e1.getClass() == e2.getClass()) return 0; //if you wish to have some extra sort order return e1.getClass().getName().compareTo(e2.getClass().getName()); } } 

.,.

 Set mySet = new TreeSet<Object>(new MyComparator()); mySet.add(new Object()); mySet.add(new Object());//same class already in set mySet.add("wtf"); //mySet.size() is now 2 - the second "new Object()" was not inserted due to the comparator check 
+2
source

Why is it so complicated?

 public class Main { public static void main(String[] args) { final Class<?> helloClass = "Hello".getClass(); final Class<?> worldClass = "World".getClass(); final Class<?> intClass = Integer.class; System.out.println(helloClass.equals(worldClass)); // -> true System.out.println(helloClass.equals(intClass)); // -> false } } 
+1
source

You can save the list of members in Set .

 public static class MyGenericClass<T extends MyGenericClass.MyInterface> { private List<T> members = new ArrayList<>(0); // Add this. private Set<Class<?>> roster = new HashSet<>(); public void add(T t) { if (!roster.contains(t.getClass())) { members.add(t); roster.add(t.getClass()); } } private void soundOff() { for (T t : members) { t.doSomething(); } } public interface MyInterface { void doSomething(); } } private static class Performer implements MyGenericClass.MyInterface { final int n; public Performer(int n) { this.n = n; } @Override public void doSomething() { System.out.println("Hi, I am a " + this.getClass().getSimpleName() + "(" + n + ")"); } } private static class Performer1 extends Performer { public Performer1(int n) { super(n); } } private static class Performer2 extends Performer { public Performer2(int n) { super(n); } } private static class Performer3 extends Performer { public Performer3(int n) { super(n); } } public void test() { MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>(); myGenericClass.add(new Performer1(1)); myGenericClass.add(new Performer2(2)); myGenericClass.add(new Performer3(3)); myGenericClass.add(new Performer3(4)); // should not be inserted! myGenericClass.soundOff(); } 
0
source

You can implement Wrapper, which provides the necessary comparison and adds a wrapped instance to the set. Thus, you do not need to override equals and hashcode in your specific Performer classes, and you do not need to subclass the specific Set implementation (with which you are associated. When you subclass a HashSet you must use this specific class. But what if you want to use LinkedHashSet in at some point? You also have to override LinkedHashSet ), which can be fragile as you have to make sure that the overridden method is consistent with the rest of the class.

 class MyGenericClass<T extends MyInterface> { private Set<ClassCompareWrapper<T>> members = new HashSet<>(); public void add(T t) { members.add(new ClassCompareWrapper<T>(t)); } } class ClassCompareWrapper<T> { T t; public ClassCompareWrapper(T t) { this.t = t; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ClassCompareWrapper)) return false; ClassCompareWrapper<?> that = (ClassCompareWrapper<?>) o; return Objects.equals(t.getClass(), that.t.getClass()); } @Override public int hashCode() { return Objects.hash(t.getClass()); } @Override public String toString() { return "Wrapper{" + "t=" + t + '}'; } } 
0
source

Here are a few other ideas.

Using streams:

 public void add(T t) { if (!members.stream().anyMatch(m -> m.getClass() == t.getClass())) { members.add(t); } } 

Using AbstractSet and HashMap :

 class ClassSet<E> extends AbstractSet<E> { private final Map<Class<?>, E> map = new HashMap<>(); @Override public boolean add(E e) { // this can be // return map.putIfAbsent(e.getClass(), e) != null; // in Java 8 Class<?> clazz = e.getClass(); if (map.containsKey(clazz)) { return false; } else { map.put(clazz, e); return true; } } @Override public boolean remove(Object o) { return map.remove(o.getClass()) != null; } @Override public boolean contains(Object o) { return map.containsKey(o.getClass()); } @Override public int size() { return map.size(); } @Override public Iterator<E> iterator() { return map.values().iterator(); } } 

A HashMap can also be used without wrapping it in Set . The Set interface is defined around equals and hashCode , so any implementation that differs from this is technically non-contractual. Alternatively, you can use LinkedHashMap if the values ​​are repeated frequently.

0
source

All Articles