为什么java.util.Set<V>接口没有提供get(Object o)方法?

49

我知道根据.equals(),Set只允许一个对象的实例存在,并且如果您已经拥有相等的对象,您不应该需要从Set中获取对象,但我仍然想要一个.get()方法,它可以返回Set中与给定参数相等的对象实例(或null)。

有关其设计原因的任何想法/理论?

通常,我必须通过使用Map并使键和值相同或类似的方法来绕过此问题。

编辑:到目前为止,我认为人们不理解我的问题。 我想要的是已经在集合中的确切对象实例,而不是.equals()返回true的可能不同的对象实例。

至于为什么我希望出现这种行为,通常.equals()不能考虑对象的所有属性。 我想提供一些虚拟查找对象,并获取Set中的实际对象实例。


我真的很想这样做,但是使用Map的键。就像,我希望Map.getKey(K k)返回k',其中k.equals(k')。我能用你的hack来做到这一点吗?还是我必须制作一个Pair<K,V>,并将我的Map<K,V>更改为Map<K,Pair<K,V>>? - Jayen
与大多数其他集合类型不同,集合通常不是检索特定元素,而是测试一个值是否属于该集合。 - GClaramunt
24个回答

0
如果您已在Java Bug Parade列表中请求此功能,请在此处列出并让我们投票支持。我认为至少方便类java.util.Collections只需获取一个集合和一个对象,就可以实现它。
searchSet(Set ss, Object searchFor){

        Iterator it = ss.iterator();
        while(it.hasNext()){
            Object s = it.next();
            if(s != null && s.equals(searchFor)){
                return s;
            }
        }

0

列表是一种有序的数据结构,因此它遵循插入顺序。因此,您放置的数据将在您插入时的确切位置上可用。

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

list.get(0); // will return value 1

把它当作简单的数组记住。

集合是无序的数据结构。因此它没有遵循任何顺序。您插入到某个位置的数据将在任何位置都可用。

Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
//assume it has get method
set.get(0); // what are you expecting this to return. 1?.. 

但它将返回其他内容。因此,在Set中创建get方法没有任何意义。
**注意**:我在解释中使用了int类型,这同样适用于Object类型。

2
你认为 Set.get(int index) 不适用(忽略干扰的操作,如 clear()remove()add(int index, E element)) - 问题是关于 Set.get(Object o) 的。(主要是为了保持这个问题不会受到太多的关注。) - greybeard
这同样适用于 Object 类型。请为成员显示其他内容:没有定义/声明,我不知道 set.get(0) 中的 0 是索引还是键 - 如果我知道了,仍然可能会令人困惑。考虑使用字符串字面量。 - greybeard

0

这只是一个观点。我认为我们需要明白,有些Java类没有字段/属性,即仅有方法。在这种情况下,equals不能通过比较函数来衡量,其中一个例子就是requestHandlers。请看下面这个JAX-RS应用程序的示例。在这种情况下,SET比任何数据结构都更有意义。

@ApplicationPath("/")
public class GlobalEventCollectorApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(EventReceiverService.class);
        classes.add(VirtualNetworkEventSerializer.class);
        return classes;
    }
}

回答你的问题,如果你有一个浅层员工对象(即只有 EMPID,用于在 equals 方法中确定唯一性),并且如果你想通过在集合中查找来获取深层对象,SET 不是正确的数据结构,因为它的目的不同。

0

“我想要的是已经在集合中的确切对象实例,而不是可能返回true的不同对象实例。”

这没有意义。假设你这样做:

Set<Foo> s = new Set<Foo>();
s.Add(new Foo(...));
...
Foo newFoo = ...;

你现在做:

s.contains(newFoo)

如果你希望只有当集合中的一个对象等于newFoo时才为真,那么需要使用对象标识实现Foo的equals和hashCode方法。或者,如果你想将多个相等的对象映射到一个规范的原始对象,则Map可能是正确的选择。

1
“不合理”是什么意思?如果newFoo.equals(originalFoo),那么s.contains(newFoo)应该为true,就像.contains()的定义一样。他只想得到他拥有的originalFoo,而不是newFoo - user102008

0

我曾经遇到过同样的问题。我通过将我的集合转换为 Map,然后从 Map 中获取它们来解决了这个问题。我使用了以下方法:

public Map<MyObject, MyObject> convertSetToMap(Set<MyObject> set)
{
    Map<MyObject, MyObject> myObjectMap = new HashMap<MyObject, MyObject>();

    for(MyObject myObject: set){
        myObjectMap.put(myObject, myObject);
    }
    return myObjectMap
}

现在你可以通过调用这个方法来从你的集合中获取元素:

convertSetToMap(myset).get(myobject);

您可以在您的类中重写equals方法,让它只检查某些属性,如Id或名称。


0

我同意,我想看到Set实现提供一个get()方法。

作为一种选项,在您的对象实现(或可以实现)java.lang.Comparable的情况下,您可以使用TreeSet。然后,通过调用ceiling()或floor()来获取get()类型的函数,并检查结果是否非空且等于比较对象,例如:

TreeSet myTreeSet<MyObject> = new TreeSet();
:
:

// Equivalent of a get() and a null-check, except for the incorrect value sitting in
// returnedMyObject in the not-equal case.
MyObject returnedMyObject = myTreeSet.ceiling(comparisonMyObject);

if ((null != returnedMyObject) && returnedMyObject.equals(comparisonMyObject)) {
:
:
}

0

我知道在Set中,根据.equals()方法只允许存在一个对象的实例,并且如果你已经有了一个等价的对象,你就不应该“需要”从Set中获取对象。但是,我仍然希望有一个.get()方法,可以根据等价的对象作为参数返回Set中的实际对象实例(或null)。


0

简单的接口/ API 在实现过程中提供更多的自由。例如,如果将 Set 接口仅减少为单个 contains() 方法,则可以获得功能编程典型的集合定义 - 它只是一个谓词,实际上没有对象被存储。对于 java.util.EnumSet 也是如此 - 它仅包含每个可能值的位图。


0

这显然是Set API的一个缺陷。

简单来说,我想在我的Set中查找一个对象并更新它的属性。

但我必须循环遍历我的(Hash)Set才能找到我的对象... 唉...


0

为什么没有get方法很简单:

如果你需要从集合中获取对象X,那是因为你需要X中的某些东西,而你没有这个对象。

如果你没有这个对象,那么你需要一些手段(键)来定位它。比如它的名称、一个数字等等。这就是映射的作用对吧。

map.get("key") -> X!

集合没有键,你需要遍历它们才能获取对象。

那么,为什么不添加一个方便的get(X) -> X呢?

这没有任何意义,因为你已经有了X,纯粹主义者会说。

但现在把它看作非纯粹主义者,看看你是否真的想要这个:

假设我创建了一个与X相等的对象Y,那么set.get(Y)->X。那么我就可以访问我没有的X的数据。例如,X有一个名为get flag()的方法,我想要它的结果。

现在看看这段代码。

Y

X = map.get(Y);

所以 Y.equals(x) 为真!

但是...

Y.flag() == X.flag() = false. (它们不相等吗?)

所以,你看,如果集合允许你像这样获取对象,那么它肯定会破坏 equals 的基本语义。之后,你将与一堆小的 X 克隆体共存,他们都声称自己是相同的,但实际上并不是。

你需要一个映射表,来存储东西并使用键来检索它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接