我理解在一个Set中只允许根据.equals()的任何对象的一个实例,并且如果你已经有一个等效的对象,你不应该“需要”从Set中获取一个对象,但我会仍然希望有一个.get()方法,该方法在给定等效对象作为参数的情况下返回Set中的对象的实际实例(或null)。
关于它为何如此设计的任何想法/理论?
我通常不得不通过使用Map并使键和值相同或类似的东西来解决这个问题。
编辑:到目前为止,我认为人们不理解我的问题。我想要一个已经在集合中的确切对象实例,而不是一个可能不同的对象实例,其中.equals()返回true。至于为什么我会想要这种行为,通常.equals()不会考虑对象的所有属性。我想提供一些虚拟查找对象并返回Set中的实际对象实例。
答案 0 :(得分:30)
虽然pure参数确实使方法get(Object)
怀疑,但潜在的意图并非没有实际意义。
有各种类和接口系列稍微重新定义equals(Object)
。只需要集合界面就可以了。例如,ArrayList和LinkedList可以相等;它们各自的内容只需要相同和相同的顺序。
因此,有很好的理由在集合中找到匹配元素。也许更明确的指示意图的方法是使用像
这样的方法public interface Collection<E> extends ... {
...
public E findMatch(Object o) throws UnsupportedOperationException;
...
}
请注意,此API的值大于Set中的值。
至于问题本身,我没有任何关于为什么省略这种操作的理论。我会说最小生成集参数不成立,因为集合API中定义的许多操作都是出于方便和高效的原因。
答案 1 :(得分:12)
问题是:Set不是用于“获取”对象,而是用于添加和测试存在。 我理解你在寻找什么,我有类似的情况,并在关键和价值中使用相同对象的地图结束。
编辑:只是为了澄清:http://en.wikipedia.org/wiki/Set_(abstract_data_type)
答案 2 :(得分:7)
几年前我在java论坛上遇到了同样的问题。他们告诉我定义了Set接口。它无法更改,因为它将破坏Set接口的当前实现。然后,他们开始声称废话,就像你在这里看到的那样:&#34; Set不需要get方法&#34;并开始钻我,Map必须始终用于从集合中获取元素。
如果你只使用set进行数学运算,比如intersection或union,那么可能是contains()就足够了。但是,Set在集合中定义以存储数据。我在Set中使用关系数据模型解释了需要get()。
在下文中,SQL表就像一个类。列定义属性(在Java中称为字段),记录表示类的实例。这样一个对象就是一个字段向量。一些字段是主键。它们定义了对象的唯一性。这就是你为Java中的contains()做的事情:
class Element {
public int hashCode() {return sumOfKeyFields()}
public boolean equals(Object e) {keyField1.equals(e) && keyField2.equals(e) && ..}
我不知道DB内部。但是,在定义表时,只指定一次键字段。您只需使用@primary注释关键字段。在向表中添加记录时,不会第二次指定键。您不像在映射中那样将键与数据分开。 SQL表是集合。它们不是地图。然而,除了保持唯一性和contains()检查之外,它们还提供了get()。
在计算机编程艺术&#34;中,引入搜索,D。Knuth说同样的话:
本章的大部分内容都致力于研究一个非常简单的搜索 问题:如何查找已存储给定的数据 鉴定。
您看,数据存储时带有标识。不是指向数据的标识,而是带标识的数据。他继续说道:
例如,在我们可能想要的数字应用程序中 找到f(x),给定x和f的值表;在一个 非数字应用,我们可能想找到英文 给定俄语单词的翻译。
看起来他开始谈论制图。但是,
一般来说,我们假设已经存储了一组N条记录, 问题是找到合适的一个。我们通常要求 N个键是不同的,因此每个键唯一地标识它 记录。所有记录的集合称为表或文件, 其中的单词&#34; table&#34;通常用于表示一个小文件,和 &#34;文件&#34;通常用于表示大表。一个大文件或 文件组通常称为数据库。
用于搜索的算法以所谓的参数K表示, 问题是找到哪个记录以K为关键。虽然 搜索的目的是找到存储在记录中的信息 与K相关联,本章中的算法通常会忽略 除钥匙本身以外的一切。在实践中我们可以找到 我们找到K后的相关数据;例如,如果K出现在 location TABLE + i,关联数据(或指向它的指针)可能是 在位置TABLE + i + 1
也就是说,搜索找到记录的密钥字段,它不应该&#34; map&#34;数据的关键。两者都位于相同的记录中,作为java对象的文件。也就是说,搜索算法检查记录的关键字段,就像在集合中一样,而不是像在地图中那样检查一些远程密钥。
我们有N个要分类的项目;我们称之为记录,和 整个N条记录集将被称为文件。每 记录Rj有一个密钥Kj,它管理排序过程。额外 除了密钥之外,数据通常也存在;这个额外的卫星 信息&#34;除了必须携带之外,对排序没有影响 作为每条记录的一部分。
我也认为没有必要在一个额外的&#34;密钥集中复制密钥&#34;在他对排序的讨论中。
... [&#34;计算机编程艺术&#34;,第6章,简介]
实体集是集合或设置特定实体类型的所有实体 [http://wiki.answers.com/Q/What_is_entity_and_entity_set_in_dbms] 单个类的对象共享其类属性。同样,在DB中做记录。它们共享列属性。
集合的一个特例是类范围,即 属于该类的所有对象的集合。类范围允许 要被视为关系的类
... [&#34;数据库系统概念&#34;,第6版]
基本上,class描述了其所有实例共有的属性。关系数据库中的表也是如此。 "The easiest mapping you will ever have is a property mapping of a single attribute to a single column."我正在谈论这种情况。
我在证明对象和数据库记录之间的类比(同构)时非常冗长,因为有些愚蠢的人不接受它(为了证明他们的Set不能有 get 方法)
你在回放中看到那些不理解这一点的人怎么说Set with get会多余?这是因为他们用来代替集合的滥用地图引入了冗余。他们对put(obj.getKey(),obj)的调用存储了两个键:原始键作为对象的一部分,以及它在地图键集中的副本。重复是冗余。它还涉及代码中的更多膨胀并浪费运行时消耗的内存。我不知道数据库内部,但良好设计和数据库规范化的原则说这种重复是个坏主意 - there must be only one source of truth。冗余意味着可能发生不一致:键映射到具有不同键的对象。不一致是冗余的表现。 Edgar F. Codd proposed DB normalization只是为了摆脱冗余及其推断的不一致性。教师明确规范化:Normalization will never generate two tables with a one-to-one relationship between them. There is no theoretical reason to separate a single entity like this with some fields in a single record of one table and others in a single record of another table
所以,我们有4个参数,为什么使用map来实现get in set是不好的:
即使你不了解记录集的想法和数据规范化,玩集合,你也可以自己发现这个数据结构和算法,就像我们org.eclipse.KeyedHashSet和C ++ STL设计者那样。
我被Sun论坛禁止指出这些想法。偏见是反对这个理由的唯一论据,这个世界由偏执者主宰。他们不希望看到概念以及事物如何不同/改进。他们只看到现实世界,无法想象Java Collections的设计可能存在缺陷并且可以改进。向这些人提醒理性事物是危险的。如果你不服从,他们会教导你他们的失明和惩罚。
2013年12月添加:SICP also says that DB is a set with keyed records rather than a map:
典型的数据管理系统花费大量时间 访问或修改记录中的数据,因此需要 一种访问记录的有效方法。这是通过识别来完成的 每条记录的一部分作为识别密钥。现在我们代表 数据库作为一组记录。
答案 3 :(得分:1)
好吧,如果你已经从套装中“拿到”了东西,你不需要得到()它,是吗? ; - )
我认为你使用地图的方法是正确的。听起来你正试图通过equals()方法“规范化”对象,我总是按照你的建议使用Map完成。
答案 4 :(得分:1)
我不确定你是否正在寻找一个解释为什么集合这样做的原因,或者是为了解决它所带来的问题的简单解决方案。其他答案涉及前者,因此这是对后者的建议。
您可以迭代Set的元素,并使用equals()方法测试它们中的每一个是否相等。它易于实现且几乎不容易出错。显然,如果您不确定元素是否在集合中,请事先使用contains()方法进行检查。
与例如HashSet的contains()方法相比,这是无效的,它会“找到”存储的元素,但不会返回它。如果您的集合可能包含许多元素,那么甚至可能会使用“更重”的解决方法,例如您提到的地图实现。但是,如果它对你来说很重要(我确实看到了拥有这种能力的好处),那么它可能是值得的。
答案 5 :(得分:1)
所以我知道你可能有两个相同的对象,但它们不是同一个实例。
如
Integer a = new Integer(3);
Integer b = new Integer(3);
在哪种情况下a.equals(b)因为它们引用相同的内在值但是a = = b,因为它们是两个不同的对象。
Set的其他实现,例如IdentitySet,它们在项目之间进行不同的比较。
但是,我认为您正在尝试将不同的哲学应用于Java。如果你的对象是相等的(a.equals(b))虽然a和b有不同的状态或含义,但这里有一些错误。您可能希望将该类拆分为两个或多个实现公共接口的语义类 - 或者重新考虑.equals和.hashCode。
如果你有Joshua Bloch的Effective Java,请看一下名为“当超越平等时遵守一般合同”和“尽量减少可变性”的章节。
答案 6 :(得分:1)
只需使用Map解决方案...... TreeSet和HashSet也可以这样做,因为它们由TreeMap和HashMap备份,所以这样做没有任何惩罚(实际上它应该是一个最小的增益)。
您也可以扩展您喜欢的Set以添加get()方法。
[]]
答案 7 :(得分:1)
我认为,给定一些Set实现,你唯一的解决方案是遍历其元素以找到一个等于() - 然后你在Set中匹配的实际对象。
K target = ...;
Set<K> set = ...;
for (K element : set) {
if (target.equals(element)) {
return element;
}
}
答案 8 :(得分:1)
如果您将其视为数学集合,则可以找到找到对象的方法 将集合与仅包含要查找的对象的对象集合相交。如果交集不为空,则集合中剩下的唯一项目就是您要查找的项目。
public <T> T findInSet(T findMe, Set<T> inHere){
inHere.retainAll(Arrays.asList(findMe));
if(!inHere.isEmpty){
return inHere.iterator().next();
}
return null;
}
它不是最有效的内存使用,但它在功能上和数学上都是正确的。
答案 9 :(得分: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类型。
答案 10 :(得分: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不是数据结构,因为它的目的不同。
答案 11 :(得分:0)
简单的接口/ API在实现过程中提供了更多的自由。例如,如果将a5
接口简化为单goto
方法,我们会得到一个典型的函数式编程定义 - 它只是一个谓词,实际上没有存储任何对象。 java.util.EnumSet也是如此 - 它只包含每个可能值的位图。
答案 12 :(得分:0)
我知道在一个Set中只允许根据.equals()的任何对象的一个实例,并且你不应该&#34;需要&#34;如果你已经拥有一个等效的对象,从Set中获取一个对象,但是我仍然想要一个.get()方法,该方法在给定一个等价对象作为参数的情况下返回Set中的对象的实际实例(或null)。
答案 13 :(得分:0)
没有得到的原因很简单:
如果你需要从集合中获取对象X是因为你需要X中的东西而你没有对象。
如果您没有该对象,则需要一些方法(键)来定位它。 ..名字,数字,永远。那是什么地图是正确的。
map.get(“key”) - &gt; X!
设置没有键,你需要遍历它们来获取对象。
那么,为什么不添加一个方便的get(X) - &gt; X
这没有道理,因为你已经有了X,纯粹主义者会说。
但现在把它视为非纯粹主义者,看看你是否真的想要这个:
假设我创建了对象Y,它与X的等号匹配,因此set.get(Y) - &gt; X. Volia,然后我可以访问我没有的X数据。比如说X有一个名为get flag()的方法,我希望得到结果。
现在看看这段代码。
ý
X = map.get(Y);
所以Y.equals(x)是真的!
,但..
Y.flag()== X.flag()= false。 (他们不等于吗?)
所以,你看,如果set允许你得到像这样的对象肯定会打破equals的基本语义。之后你会生活在X的所有小克隆中,所以当它们不存在时它们是相同的。
你需要一张地图,存储东西并使用钥匙来检索它。
答案 14 :(得分:0)
我同意我希望Set实现提供get()方法。
作为一个选项,在您的Objects实现(或可以实现)java.lang.Comparable的情况下,您可以使用TreeSet。然后可以通过调用ceiling()或floor()来获取get()类型函数,然后检查结果是非null并且等于比较对象,例如:
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)) {
:
:
}
答案 15 :(得分:0)
这显然是Set API的缺点。
简单地说,我想在我的Set中查找一个对象并更新其属性。
我必须循环通过我的(哈希)套装才能找到我的对象......叹息......
答案 16 :(得分: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;
}
}
答案 17 :(得分:0)
我遇到了同样的问题。我通过将我的设置转换为地图,然后从地图中获取它来修复它。我用这个方法:
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);
您可以覆盖类中的等号,让它只检查ID或名称等特定属性。
答案 18 :(得分:0)
说,我有一个带有ID和名称的用户POJO。 ID保持equals和hashcode之间的契约。 name不是对象相等的一部分。 我想根据某个地方的输入更新用户名,比如说UI。
由于java set不提供get方法,我需要迭代代码中的set并在找到相等对象时更新名称(即ID匹配时)。
如果你有方法,可以缩短这段代码。
Java现在带有各种愚蠢的东西,比如javadb和增强的for循环,我不明白为什么在这种特殊情况下它们是纯粹的。
答案 19 :(得分:0)
Object fromSet = set.tailSet(obj).first();
if (! obj.equals(fromSet)) fromSet = null;
做你想要的。我不知道为什么java会隐藏它。
答案 20 :(得分:0)
Functional Java有一个持久集的实现(由红/黑树支持),偶然包含split方法,似乎可以做你想要的。它返回一个三元组:
Option
的对象,该对象为空或包含找到的对象(如果它存在于集合中)。你会做这样的事情:
MyElementType found = hayStack.split(needle)._2().orSome(hay);
答案 21 :(得分:0)
我认为期望是equals真正代表一些平等,而不仅仅是两个对象具有相同的主键,例如。如果equals表示两个非常相等的对象,那么get将是多余的。你想要的用例建议一个Map,也许是一个不同的键值,代表一个主键,而不是整个对象,然后相应地正确实现equals和hashcode。
答案 22 :(得分:0)
“我想要一个已经在集合中的确切对象实例,而不是一个可能不同的对象实例,其中.equals()返回true。”
这没有意义。说你做:
Set<Foo> s = new Set<Foo>();
s.Add(new Foo(...));
...
Foo newFoo = ...;
你现在这样做:
s.contains(newFoo)
如果你希望只有当集合中的对象是== newFoo时才这样,那么实现Foo的equals和带有对象标识的hashCode。或者,如果您尝试将多个相等的对象映射到规范原件,那么Map可能是正确的选择。
答案 23 :(得分:-6)
我认为你已回答了自己的问题:这是多余的。
Set提供Set#contains(Object o),它提供所需Set#get(Object o)的等效身份测试,并返回一个布尔值,如预期的那样。