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


当前回答

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

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

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

其他回答

如果元素是相等的,那么获取它就没有意义了。Map更适合这个用例。


如果你仍然想找到元素,你没有其他选择,只能使用迭代器:

public static void main(String[] args) {

    Set<Foo> set = new HashSet<Foo>();
    set.add(new Foo("Hello"));

    for (Iterator<Foo> it = set.iterator(); it.hasNext(); ) {
        Foo f = it.next();
        if (f.equals(new Foo("Hello")))
            System.out.println("foo found");
    }
}

static class Foo {
    String string;
    Foo(String string) {
        this.string = string;
    }
    @Override
    public int hashCode() { 
        return string.hashCode(); 
    }
    @Override
    public boolean equals(Object obj) {
        return string.equals(((Foo) obj).string);
    }
}

如果你有一个相等的对象,为什么你需要集合中的一个?如果它仅与键“相等”,则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);

我在那里做过!!如果你正在使用番石榴,一个快速的方法将它转换为地图是:

Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);

使用Java 8,你可以做到:

Foo foo = set.stream().filter(item->item.equals(theItemYouAreLookingFor)).findFirst().get();

但是要小心,.get()会抛出一个NoSuchElementException,或者你可以操作一个Optional项。

因为Set的任何特定实现都可能是随机访问,也可能不是。

You can always get an iterator and step through the Set, using the iterators' next() method to return the result you want once you find the equal element. This works regardless of the implementation. If the implementation is NOT random access (picture a linked-list backed Set), a get(E element) method in the interface would be deceptive, since it would have to iterate the collection to find the element to return, and a get(E element) would seem to imply this would be necessary, that the Set could jump directly to the element to get.

当然,Contains()可能需要做同样的事情,也可能不需要,这取决于实现,但名称似乎不会导致同样的误解。