有人可以给我指一下正确的方向吗: 我想要一个自定义的 HashSet
,但不想改变 hashCode()/equals()
方法。
使用场景是需要一个对象集合,这些对象必须具有不同的一个或多个属性。
例如,对于这个类:
@FieldDefaults(level = AccessLevel.PRIVATE)
@Getter @Setter
public class User{
String name;
String email;
String age;
}
我想要有一个UserNameSet
,它只允许包含具有不同姓名的用户。我不想在User类中重写hashCode和equals方法,因为我仍然希望区分具有相同名称但电子邮件不同的用户。
我想要以某种方式“覆盖”这个HashMap
的hashCode()/equals()
方法,仅限于这一个HashMap
。
编辑:
我看到了这个解决方案,一开始似乎可以工作,能否有人检查一下?
package com.znamenacek.debtor.util;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@FieldDefaults(level = AccessLevel.PRIVATE)
public class CustomizableHashSet<T> implements Set<T> {
Function<T, Integer> customHashCode = Object::hashCode;
HashSet<ClassWrapper> storage = new HashSet<>();
public CustomizableHashSet(Function<T, Integer> customHashCode) {
this.customHashCode = customHashCode;
}
public CustomizableHashSet() {}
public CustomizableHashSet(Collection<? extends T> c, Function<T, Integer> customHashCode) {
storage = new HashSet<>(c.stream().map(ClassWrapper::new).toList());
this.customHashCode = customHashCode;
}
public CustomizableHashSet(Collection<? extends T> c) {
storage = new HashSet<>(c.stream().map(ClassWrapper::new).toList());
}
public CustomizableHashSet(int initialCapacity, float loadFactor, Function<T, Integer> customHashCode) {
storage = new HashSet<>(initialCapacity, loadFactor);
this.customHashCode = customHashCode;
}
public CustomizableHashSet(int initialCapacity, float loadFactor) {
storage = new HashSet<>(initialCapacity, loadFactor);
}
public CustomizableHashSet(int initialCapacity, Function<T, Integer> customHashCode) {
storage = new HashSet<>(initialCapacity);
this.customHashCode = customHashCode;
}
public CustomizableHashSet(int initialCapacity) {
storage = new HashSet<>(initialCapacity);
}
@Override
public Iterator<T> iterator() {
return storage.stream().map(ClassWrapper::get).iterator();
}
@Override
public int size() {
return storage.size();
}
@Override
public boolean isEmpty() {
return storage.isEmpty();
}
@Override
public boolean contains(Object o) {
return storage.stream().map(ClassWrapper::get).collect(Collectors.toSet()).contains(o);
}
@Override
public boolean add(T t) {
return storage.add(new ClassWrapper(t));
}
@Override
public boolean remove(Object o) {
boolean returnValue;
var storageContent = storage.stream().map(ClassWrapper::get).collect(Collectors.toSet());
returnValue = storageContent.remove(o);
storage = storageContent.stream().map(ClassWrapper::new).collect(Collectors.toCollection(HashSet::new));
return returnValue;
}
@Override
public void clear() {
storage.clear();
}
@Override
public Object clone() {
throw new UnsupportedOperationException();
}
@Override
public Spliterator<T> spliterator() {
return storage.stream().map(ClassWrapper::get).spliterator();
}
@Override
public Object[] toArray() {
return storage.stream().map(ClassWrapper::get).toArray();
}
@Override
public <T1> T1[] toArray(T1[] a) {
return storage.stream().map(ClassWrapper::get).collect(Collectors.toSet()).toArray(a);
}
@Override
public boolean removeAll(Collection<?> c) {
boolean returnValue;
var storageContent = storage.stream().map(ClassWrapper::get).collect(Collectors.toSet());
returnValue = storageContent.removeAll(c);
storage = storageContent.stream().map(ClassWrapper::new).collect(Collectors.toCollection(HashSet::new));
return returnValue;
}
@Override
public boolean containsAll(Collection<?> c) {
boolean returnValue;
var storageContent = storage.stream().map(ClassWrapper::get).collect(Collectors.toSet());
returnValue = storageContent.containsAll(c);
storage = storageContent.stream().map(ClassWrapper::new).collect(Collectors.toCollection(HashSet::new));
return returnValue;
}
@Override
public boolean addAll(Collection<? extends T> c) {
return storage.addAll(c.stream().map(ClassWrapper::new).collect(Collectors.toSet()));
}
@Override
public boolean retainAll(Collection<?> c) {
boolean returnValue;
var storageContent = storage.stream().map(ClassWrapper::get).collect(Collectors.toSet());
returnValue = storageContent.retainAll(c);
storage = storageContent.stream().map(ClassWrapper::new).collect(Collectors.toCollection(HashSet::new));
return returnValue;
}
@Override
public String toString() {
return storage.stream().map(ClassWrapper::get).collect(Collectors.toSet()).toString();
}
@Override
public <T1> T1[] toArray(IntFunction<T1[]> generator) {
return storage.stream().map(ClassWrapper::get).collect(Collectors.toSet()).toArray(generator);
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
boolean returnValue;
var storageContent = storage.stream().map(ClassWrapper::get).collect(Collectors.toSet());
returnValue = storageContent.removeIf(filter);
storage = storageContent.stream().map(ClassWrapper::new).collect(Collectors.toCollection(HashSet::new));
return returnValue;
}
@Override
public Stream<T> stream() {
return storage.stream().map(ClassWrapper::get);
}
@Override
public Stream<T> parallelStream() {
return storage.parallelStream().map(ClassWrapper::get);
}
@Override
public void forEach(Consumer<? super T> action) {
storage.stream().map(ClassWrapper::get).forEach(action);
}
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@AllArgsConstructor
public class ClassWrapper{
T object;
@Override
public int hashCode() {
return customHashCode.apply(object);
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null) return false;
return hashCode() == obj.hashCode();
}
public T get(){
return object;
}
@Override
public String toString() {
return "" + hashCode() + " - " + object.toString();
}
}
}
HashMap<String, User>
,将名称映射到完整的用户,然后从该映射中获取值。 - tobias_kUserDecorator
(带有重写的hashCode()
和equals()
)听起来很合理。 - Elliott FrischHashSet
,TreeSet
有一个可以接受自定义比较器的构造函数。 - Johannes Kuhn