为什么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。


当前回答

将set转换为list,然后使用list的get方法

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);

其他回答

如果你有一个相等的对象,为什么你需要集合中的一个?如果它仅与键“相等”,则Map将是更好的选择。

不管怎样,下面的方法就可以了:

Foo getEqual(Foo sample, Set<Foo> all) {
  for (Foo one : all) {
    if (one.equals(sample)) {
      return one;
    }
  } 
  return null;
}

在Java 8中,这可以变成一行代码:

return all.stream().filter(sample::equals).findAny().orElse(null);

你可以使用Iterator类

import java.util.Iterator;
import java.util.HashSet;

public class MyClass {
 public static void main(String[ ] args) {
 HashSet<String> animals = new HashSet<String>();
animals.add("fox");
animals.add("cat");
animals.add("dog");
animals.add("rabbit");

Iterator<String> it = animals.iterator();
while(it.hasNext()) {
  String value = it.next();
  System.out.println(value);   
 }
 }
}

哈希码的契约清楚地表明:

如果根据Object方法,两个对象是相等的,那么在这两个对象上调用hashCode方法必须产生相同的整数结果。

所以你的假设是

为了澄清,equals方法被重写,但它只检查其中之一 田地,不是全部。所以两个相等的Foo对象可以 有不同的值,这就是为什么我不能只使用foo。”

是错误的,你违反了合同。如果我们看Set接口的"contains"方法,我们有:

boolean contains(Object o); 如果此集合包含指定的元素,则返回true。更多的 形式上,当且仅当此集合包含元素时返回true "e"使得o==null ?E ==null: o. = (E)

为了实现您想要的效果,您可以使用Map,在其中定义键并使用定义对象如何彼此不同或相等的键存储元素。

看起来合适的使用对象是番石榴中的Interner:

为其他不可变提供与String.intern()相同的行为 类型。常见的实现可以从Interners获得 类。

它也有一些非常有趣的杠杆,比如concurrencyLevel,或者使用的引用类型(可能值得注意的是,它没有提供softinternet,我认为这比weakinternet更有用)。

如果你看一下java.util.HashSet实现的前几行,你会看到:

public class HashSet<E>
    ....
    private transient HashMap<E,Object> map;

HashSet在内部使用HashMap,这意味着如果你直接使用HashMap并使用与键相同的值,你会得到你想要的效果,并节省一些内存。