为什么Set不提供获取与另一个元素相等的元素的操作?
Set<Foo> set = ...;
...
Foo foo = new Foo(1, 2, 3);
Foo bar = set.get(foo); // get the Foo element from the Set that equals foo
我可以问Set是否包含一个等于bar的元素,那么为什么我不能得到那个元素呢?:(
为了澄清,equals方法被重写,但它只检查其中一个字段,而不是所有字段。两个相等的Foo对象可以有不同的值,这就是为什么我不能只用Foo。
如果你有一个NavigableSet(例如TreeSet),你可以这样做:
public static <E> E get(NavigableSet<E> set, E key) {
return set.tailSet(key, true).floor(key);
}
对于HashSet及其后代(如LinkedHashSet)来说,事情有点棘手:
import java.util.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
private static final Field mapField;
private static final Method hashMethod;
private static final Method getNodeMethod;
private static final Field keyField;
static {
try {
mapField = HashSet.class.getDeclaredField("map");
mapField.setAccessible(true);
hashMethod = HashMap.class.getDeclaredMethod("hash", Object.class);
hashMethod.setAccessible(true);
getNodeMethod = HashMap.class.getDeclaredMethod("getNode",
Integer.TYPE, Object.class);
getNodeMethod.setAccessible(true);
keyField = Class.forName("java.util.HashMap$Node").getDeclaredField("key");
keyField.setAccessible(true);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static <E> E get(HashSet<E> set, E key) {
try {
Object map = mapField.get(set);
Object hash = hashMethod.invoke(null, key);
Object node = getNodeMethod.invoke(map, hash, key);
if (node == null)
return null;
@SuppressWarnings("unchecked")
E result = (E)keyField.get(node);
return result;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static <E> E get(NavigableSet<E> set, E key) {
return set.tailSet(key, true).floor(key);
}
public static void main(String[] args) {
HashSet<Integer> s = new HashSet<>();
// HashSet<Integer> s = new LinkedHashSet<>();
// TreeSet<Integer> s = new TreeSet<>();
for (int i = 0; i < 100_000; i++)
s.add(i);
Integer key = java.awt.event.KeyEvent.VK_FIND;
Integer hidden = get(s, key);
System.out.println(key);
System.out.println(hidden);
System.out.println(key.equals(hidden));
System.out.println(key == hidden);
}
}
如果你有一个NavigableSet(例如TreeSet),你可以这样做:
public static <E> E get(NavigableSet<E> set, E key) {
return set.tailSet(key, true).floor(key);
}
对于HashSet及其后代(如LinkedHashSet)来说,事情有点棘手:
import java.util.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
private static final Field mapField;
private static final Method hashMethod;
private static final Method getNodeMethod;
private static final Field keyField;
static {
try {
mapField = HashSet.class.getDeclaredField("map");
mapField.setAccessible(true);
hashMethod = HashMap.class.getDeclaredMethod("hash", Object.class);
hashMethod.setAccessible(true);
getNodeMethod = HashMap.class.getDeclaredMethod("getNode",
Integer.TYPE, Object.class);
getNodeMethod.setAccessible(true);
keyField = Class.forName("java.util.HashMap$Node").getDeclaredField("key");
keyField.setAccessible(true);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static <E> E get(HashSet<E> set, E key) {
try {
Object map = mapField.get(set);
Object hash = hashMethod.invoke(null, key);
Object node = getNodeMethod.invoke(map, hash, key);
if (node == null)
return null;
@SuppressWarnings("unchecked")
E result = (E)keyField.get(node);
return result;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static <E> E get(NavigableSet<E> set, E key) {
return set.tailSet(key, true).floor(key);
}
public static void main(String[] args) {
HashSet<Integer> s = new HashSet<>();
// HashSet<Integer> s = new LinkedHashSet<>();
// TreeSet<Integer> s = new TreeSet<>();
for (int i = 0; i < 100_000; i++)
s.add(i);
Integer key = java.awt.event.KeyEvent.VK_FIND;
Integer hidden = get(s, key);
System.out.println(key);
System.out.println(hidden);
System.out.println(key.equals(hidden));
System.out.println(key == hidden);
}
}