通用列表中的唯一类

时间:2015-07-30 08:15:38

标签: java generics generic-list generic-programming generic-collections

我有一个泛型类,里面有一个通用列表。我想确保通用列表只包含唯一的类。

到目前为止,我所做的是将类名与反射(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!";
    }
  }
}

6 个答案:

答案 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类中覆盖hashcodePerformer,并且您不必子类化具体的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);
    }
}

使用AbstractSetHashMap

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接口定义在equalshashCode附近,因此任何与此不同的实现在技术上都是非契约性的。此外,如果经常迭代值,您可能希望使用LinkedHashMap