[英]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實現。 元素按照其自然順序排序,或者在創建時創建時提供的比較器,具體取決於使用的構造函數。
使用Comparator
的Set
示例:
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
類中重寫equals
和hashcode
,並且您不必子類化具體的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);
}
}
使用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
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.