我有一个泛型类,里面有一个通用列表。我想确保通用列表只包含唯一的类。
到目前为止,我所做的是将类名与反射(getClass())进行比较。但我认为这不是一个干净的解决方案。有没有更好的检查方法?
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!";
}
}
}
答案 0 :(得分:2)
不要使用列表,而是使用java.util.Set。
不包含重复元素的集合。更正式地说,集合不包含元素e1和e2对,例如e1.equals(e2),最多只有一个null元素。
如果迭代顺序很重要或者您想使用自定义Comparator,则可以使用TreeSet实现:
基于TreeMap的NavigableSet实现。元素按照其自然顺序排序,或者在创建时创建时提供的比较器,具体取决于使用的构造函数。
使用Set
的{{1}}示例:
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());
}
}
答案 1 :(得分:2)
您可以继承java.util.Set接口实现。继承java.util.AbstractSet可能是最容易的。
默认情况下,“设置”会按照.equals()
方法比较对象 - 在您的情况下,这还不够。您需要覆盖contains
方法以确保仅添加唯一类的实例。
在你的覆盖contains
中,比较类实例而不是它们的字符串化包名称可能相同/更容易
即。使用a.getClass() == b.getClass()
,而不是a.getClass().getName()
答案 2 :(得分:1)
为什么这么复杂?
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
}
}
答案 3 :(得分:0)
您可以在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();
}
答案 4 :(得分:0)
您可以实现一个Wrapper,它提供必要的比较并将包装的实例添加到集合中。这样您就不必在具体的equals
类中覆盖hashcode
和Performer
,并且您不必子类化具体的Set实现(它与您耦合。当您子类a HashSet
,你必须使用那个具体的类。但是如果你想在某个时候使用LinkedHashSet
怎么办呢?你还必须覆盖LinkedHashSet
,这可能是脆弱的因为你必须确保被覆盖的方法与类的其余部分一致。
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 +
'}';
}
}
答案 5 :(得分:0)
以下是其他一些想法。
使用流:
public void add(T t) {
if (!members.stream().anyMatch(m -> m.getClass() == t.getClass())) {
members.add(t);
}
}
使用AbstractSet
和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();
}
}
也可以使用HashMap
而不将其包裹在Set
中。 Set
接口定义在equals
和hashCode
附近,因此任何与此不同的实现在技术上都是非契约性的。此外,如果经常迭代值,您可能希望使用LinkedHashMap
。