簡體   English   中英

通用列表中的唯一類

[英]Unique classes in generic list

我有一個泛型類,里面有一個泛型列表。 我想確保通用列表只包含唯一的類。

到目前為止,我所做的是將類名與反射(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!";
    }
  }
}

不要使用List,而是使用java.util.Set

不包含重復元素的集合。 更正式地說,集合不包含元素對e1和e2,使得e1.equals(e2)和至多一個null元素。

如果迭代順序很重要或者您想使用自定義Comparator ,則可以使用TreeSet實現:

基於TreeMap的NavigableSet實現。 元素按照其自然順序排序,或者在創建時創建時提供的比較器,具體取決於使用的構造函數。

使用ComparatorSet示例:

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

您可以繼承java.util.Set接口實現。 java.util.AbstractSet子類化可能是最容易的。

默認情況下,“Set”將通過.equals()方法比較對象 - 在您的情況下,這還不夠。 您將需要覆蓋contains方法以確保僅添加唯一類的實例。

在您的overrideen contains ,它可能與類實例相比/更容易比較,而不是它們的字符串化包名

即使用a.getClass() == b.getClass() ,而不是a.getClass().getName()

為什么這么復雜?

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
    }
}

您可以在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();
}

您可以實現一個Wrapper,它提供必要的比較並將包裝的實例添加到集合中。 這樣您就不必在具體的Performer類中重寫equalshashcode ,並且您不必子類化具體的Set實現(它與您相關聯。當您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 +
                '}';
    }
}

以下是其他一些想法。

使用流:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM