Set
不提供获取等于另一个元素的元素的操作?我没有得到那个元素吗? :( 为了澄清起见,
Set
方法被覆盖,但它仅检查一个字段,而不是全部。因此,两个被认为相等的bar
对象实际上可以具有不同的值,这就是为什么我可以做到这一点只需使用equals
。#1 楼
如果元素相等,将毫无意义。一个Map
更适合此用例。 br />评论
绝对有可能获得该元素。如果您希望在元素已添加到Set中之后更新某些元素的值该怎么办?例如,当.equals()未使用所有字段时,如指定的OP。效率较低的解决方案是删除元素,然后将其值更新后重新添加。
–凯尔
13年2月19日在17:48
我仍然会认为Map更适合(在这种情况下,Map
– dacwe
13年2月19日在20:48
@dacwe,我到达这里是因为我开始寻找一种避免这种情况的方法!同时充当键和相应值的对象正是集合的全部含义。就我而言,我想通过键(字符串)从集合中获取一些复杂的对象。此String被封装(并且是唯一的)到要映射的对象。实际上,整个对象围绕所述键“旋转”。而且,调用者知道所说的字符串,但不知道对象本身。这就是为什么它要通过键检索它的原因。我现在正在使用地图,但是它仍然是奇怪的行为。
–pauluss86
2014年1月16日19:29
@KyleM我了解用例,但是我想强调不要触摸hashCode / equals的属性的重要性。来自Set Javadoc:“注意:如果将可变对象用作set元素,则必须格外小心。如果对象的值以影响equals比较的方式更改,而对象是集合中的元素。” -我建议这些对象是不可变的,或者至少具有不可变的键属性。
– stivlo
15年2月26日在17:33
我同意您可以使用Map
–梅基
15年3月6日在17:38
#2 楼
为了回答精确的问题“为什么Set
不提供获取等于另一个元素的元素的操作?”,答案将是:因为集合框架的设计者并不十分前瞻。他们没有想到您的使用案例非常合法,只是天真地尝试“对数学集抽象进行建模”(来自javadoc),而只是忘记添加有用的get()
方法。那么如何获得元素”:我认为最好的解决方案是使用Map<E,E>
而不是Set<E>
来将元素映射到自身。这样,您可以有效地从“集合”中检索元素,因为Map
的get()方法将使用高效的哈希表或树算法来找到该元素。如果需要,您可以编写自己的Set
实现,该实现提供附加的get()
方法,将Map
封装起来。以下答案在我看来是好是坏: “您不需要获取元素,因为您已经有一个相等的对象”:断言是错误的,正如您在问题中已经表明的那样。相等的两个对象仍然可以具有与对象相等无关的不同状态。目的是访问
Set
中包含的元素的此状态,而不是用作“查询”的对象的状态。“您别无选择,只能使用迭代器” :这是对集合的线性搜索,对于大型集合而言这是完全无效的(具有讽刺意味的是,内部
Set
被组织为可以有效查询的哈希图或树)。别做!通过使用该方法,我已经看到了现实系统中的严重性能问题。在我看来,缺少get()
方法的可怕之处并不在于解决它有点麻烦,而是大多数程序员会在不考虑其含义的情况下使用线性搜索方法。评论
嗯此处,问题在于重写等于的实现,以使非等于的对象成为“等于”。要求提供一种方法,说“给我与该对象相同的对象”,然后期望返回一个不相同的对象,这似乎很疯狂,很容易引起维护问题。正如其他人所建议的那样,使用地图可以解决所有这些问题:它使您所做的事情不言自明。很容易理解,两个不相等的对象在映射中可能具有相同的键,而具有相同的键将显示它们之间的关系。
–大卫·奥格伦(David Ogren)
2014-02-10 22:27
强有力的话,@ David Ogren。嗯疯?但是在您的评论中,您使用的是“相同”和“相等”,就好像它们是同一意思。他们不。具体来说,在Java中,标识由“ ==”运算符表示,而相等由equals()方法表示。如果它们的意思相同,则根本不需要equals()方法。用其他语言,这当然可以不同。例如,在Groovy中,标识是is()方法,相等性是“ ==”。好笑,不是吗?
– jschreiner
2014-2-27 14:11
当我应该使用等效这个词时,您对我使用相同这个词的批评是非常有效的。但是,在对象上定义均等以使Foo和Bar“等于”但不足以让他等效地使用它们,这将在功能和可读性/可维护性方面造成各种问题。 Set的这个问题只是潜在问题的冰山一角。例如,相等的对象必须具有相等的哈希码。因此,他将有潜在的哈希冲突。对象专门调用.get(foo)以获取foo以外的内容是否疯狂?
–大卫·奥格伦(David Ogren)
2014年2月27日14:47
可能值得注意的是,例如HashSet被实现为HashMap的包装器(将键映射到虚拟值)。因此,显式使用HashMap代替HashSet不会导致内存使用方面的开销。
– Alexey B.
2014年6月19日在23:20
@ user686249我觉得这已经演变成一场学术辩论。我确实承认,我可能在反对压倒一切方面有些落伍。特别是在像您这样的用途中。但是,我仍然反对调用此方法get()的想法。在您的示例中,customerSet.get(thisCustomer)会让我非常困惑。 (鉴于许多答案都建议使用地图,那么使用canonicalCustomerMap.get(此客户)就可以了。我也可以使用更明确命名的方法(例如NSSet上的Objective-C的成员方法)。
–大卫·奥格伦(David Ogren)
2015年4月30日在13:34
#3 楼
如果您有一个相等的对象,那么为什么需要集合中的那个?如果仅通过键“相等”,则Map
将是一个更好的选择。无论如何,以下方法可以做到: />使用Java 8,它可以变成一个衬里:Foo getEqual(Foo sample, Set<Foo> all) {
for (Foo one : all) {
if (one.equals(sample)) {
return one;
}
}
return null;
}
评论
我更喜欢这个答案,我只会避免使用两个return语句,因为这是针对OOP的,这会使Cyclomatic Complexity值更高。
–狮子座
16 Dec 16'在18:21
@Leo谢谢,但是单出口范例并不反对OOP,并且对于比Fortran或COBOL更现代的语言而言,大多数情况下是无效的,另请参见softwareengineering.stackexchange.com/questions/118703/…
– Arne Burmeister
16 Dec 17'0:39
使用Map而不是Set似乎是一个更好的选择:遍历Set的元素比从Map中获取单个值要花更多的工作。 (O(N)与O(1))
–杰米·弗洛诺(Jamie Flournoy)
18-10-5在21:16
@JamieFlournoy,如果您必须多次检查同一集合中的不同元素,那就更好了。无需一次性使用,因为首先需要花费更多的精力来构建地图。
– Arne Burmeister
18-10-5在21:20
#4 楼
不幸的是,正如jschreiner正确解释的那样,Java中的Default Set并非旨在提供“获取”操作。使用迭代器查找感兴趣元素(由dacwe建议)或删除元素并重新添加其元素,并使用更新的值(由KyleM建议),虽然可以,但是效率非常低。正如David Ogren正确指出的那样,很容易导致维护问题。
imho使用Map作为显式替换(很多人建议),恕我直言,使代码不太优雅。
如果目标是访问集合中包含的元素的原始实例(希望我能正确理解您的用例),那么这是另一种可能的解决方案。
I使用Java开发客户端-服务器视频游戏时,个人有相同的需求。以我为例,每个客户端都有存储在服务器中的组件的副本,问题出在每当客户端需要修改服务器对象时。
通过互联网传递对象意味着客户端已经无论如何,该对象的不同实例。为了使此“复制”实例与原始实例匹配,我决定使用Java UUID。 。
此UUID在客户端和服务器实例之间共享,因此通过这种方式,只需使用Map即可轻松匹配它们。
直接使用Map在类似的用例中仍然不雅。有人可能会争辩说,使用Map进行维护和处理可能会更加复杂。
由于这些原因,我实现了一个名为MagicSet的库,该库使开发人员对Map的使用“透明”。 />
https://github.com/ricpacca/magicset
像原始的Java HashSet一样,MagicHashSet(库中提供的MagicSet的实现之一)使用支持HashMap,但不是将元素作为键并将伪值作为值,而是使用元素的UUID作为键。元素本身就是价值。与普通的HashSet相比,这不会导致内存使用方面的开销。
此外,MagicSet可以与Set完全一样使用,但是还有更多提供其他功能的方法,例如getFromId(),popFromId(),removeFromId()等。
使用它的唯一要求是,要存储在MagicSet中的任何元素都需要扩展抽象类UniqueItem。
这里是一个代码示例,试图检索该对象。 MagicSet中某个城市的原始实例,并给出该城市的另一个实例,该实例具有相同的UUID(甚至只是其UUID)。
class City extends UniqueItem {
// Somewhere in this class
public void doSomething() {
// Whatever
}
}
public class GameMap {
private MagicSet<City> cities;
public GameMap(Collection<City> cities) {
cities = new MagicHashSet<>(cities);
}
/*
* cityId is the UUID of the city you want to retrieve.
* If you have a copied instance of that city, you can simply
* call copiedCity.getId() and pass the return value to this method.
*/
public void doSomethingInCity(UUID cityId) {
City city = cities.getFromId(cityId);
city.doSomething();
}
// Other methods can be called on a MagicSet too
}
#5 楼
将集转换为列表,然后使用列表的get
方法Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);
评论
我不明白这将检索集合中的任意对象。不是对象。
– aioobe
17年8月19日在22:39
为什么会有这么多的投票?在此答案中,您将集合转换为列表并检索了第一个对象,而不是“ foo”
– Uri Loya
7月20日11:27
使其过于复杂,并使其效率非常低(消耗大量内存,速度很慢)。正确的方法是使用地图。
– zakmck
10月5日下午16:34
#6 楼
如果您的集合实际上是NavigableSet<Foo>
(例如TreeSet
)和Foo implements Comparable<Foo>
,则可以使用Foo bar = set.floor(foo); // or .ceiling
if (foo.equals(bar)) {
// use bar…
}
(感谢@ eliran-malka的注释。 )
评论
如果我不介意任何人阅读我的代码时,最初以为我完全疯了,那将是一个不错的解决方案。
–亚当
17年11月9日17:47
#7 楼
使用Java 8,您可以执行以下操作:Foo foo = set.stream().filter(item->item.equals(theItemYouAreLookingFor)).findFirst().get();
但是要小心,.get()会引发NoSuchElementException,或者您可以操纵Optional项。
评论
item-> item.equals(theItemYouAreLookingFor)可以简化为ItemYouAreLookingFor :: equals
– Henno Vermeulen
18年5月3日在6:40
#8 楼
原因:Set似乎在提供比较手段方面发挥了有用的作用。它被设计为不存储重复的元素。
由于这种意图/设计,如果要get()对存储对象的引用,然后对其进行变异,则可能是由于Set可能会受阻并可能导致意外行为。
来自JavaDocs
如果将可变对象用作set元素,则必须格外小心。如果对象的值更改为影响相等比较的方式,而该对象是集合中的元素,则不指定集合的行为。
如何:
现在已经介绍了Streams,可以执行以下操作
mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();
评论
由于Stream API的支持。尽管如此,仍然不建议使用get(),而应使用orElse()或ifPresent()。
–alistairv
12月11日在22:53
#9 楼
Object objectToGet = ...
Map<Object, Object> map = new HashMap<Object, Object>(set.size());
for (Object o : set) {
map.put(o, o);
}
Object objectFromSet = map.get(objectToGet);
如果只执行一次操作,将不会很好地执行,因为您将遍历所有元素,但是在大集合上执行多次检索时,您会注意到其中的区别。
#10 楼
您可以使用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);
}
}
}
#11 楼
为此,您最好使用Java HashMap对象http://download.oracle.com/javase/1,5.0/docs/api/java/util/HashMap.html#12 楼
我知道,这是很久以前问过的,但是,如果有人有兴趣,这是我的解决方案-由HashMap支持的自定义集类: >您可以轻松实现所有其他Set方法。
评论
最好包括示例而不是仅链接到一个示例。
–所有工人都是必不可少的
2014-2-10 22:34
@Lukas Z.您的add()不能确保该元素是否存在于“集合”中。因此,根据您的实现,允许重复!这显然违反了集合的原理。
–StudentAccount4
8月13日13:10
#13 楼
去过也做过!!如果您使用的是番石榴,将其转换为地图的快速方法是:Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);
#14 楼
如果要从HashSet中获取第n个元素,则可以采用以下解决方案,这里我在HashSet中添加了ModelClass的对象。
#15 楼
如果看一下java.util.HashSet
的实现的前几行,您会看到:直接使用与键相同的值,您将获得想要的效果并节省一些内存。#16 楼
似乎使用的正确对象是来自guava的Interner:为其他不可变类型提供与String.intern()等效的行为。常见的实现可从Interners
类中获得。
它还具有一些非常有趣的杠杆,例如concurrencyLevel或使用的引用类型(可能值得注意的是它没有提供SoftInterner,我认为它比WeakInterner更有用)。
#17 楼
那么使用Arrays类呢?import java.util.Arrays;
import java.util.List;
import java.util.HashSet;
import java.util.Arrays;
public class MyClass {
public static void main(String args[]) {
Set mySet = new HashSet();
mySet.add("one");
mySet.add("two");
List list = Arrays.asList(mySet.toArray());
Object o0 = list.get(0);
Object o1 = list.get(1);
System.out.println("items " + o0+","+o1);
}
}
输出:
项目一,二
评论
因为您没有任何索引来提取对象,所以只有“一个”。另外,它效率低下。正确的方法是使用地图。
– zakmck
10月5日下午16:36
@zakmck我运行示例,结果是项目一,二。此外,该问题并未询问有关性能的问题。
–速度
10月6日13:10
你没明白这一点。您在这个错误的示例中得到了结果,但是在现实生活中,您想要这样做:gist.github.com/marco-brandizi/4cbbb54746e5200763886cdbfa4a1466,因为您想知道对象是否存在,如果是,请进行检索。您没有索引,即使您创建了列表并用于搜索原始o,这也将是非常糟糕的代码,并且即使没有明确要求,您也应该假设人们需要有效的解决方案。
– zakmck
10月7日,11:04
#18 楼
因为Set的任何特定实现都可能是随机访问,也可能不是随机访问。您总是可以获取迭代器并逐步遍历Set,使用迭代器的
next()
方法返回找到的结果。相等的元素。无论实现如何,此方法均有效。如果实现不是随机访问(使用链接列表支持的Set),则接口中的get(E element)
方法将具有欺骗性,因为它必须迭代集合以找到要返回的元素,而get(E element)
似乎暗示了这一点。 Set可能会直接跳到要获取的元素上。contains()
当然可以或可以不必做相同的事情,具体取决于实现方式,但是名称并不似乎很容易引起同样的误会。评论
get()方法可以做的任何事情已经由contains()方法完成。您必须先获取包含的对象并调用其.equals()方法,才能实现contains()。 API设计人员似乎对将get()添加到java.util.List毫无疑虑,尽管在某些实现中会很慢。
–布莱恩·林克(Bryan Rink)
13-10-25在22:14
我不认为这是真的。两个对象通过等号可以相等,但通过==可以不相等。如果您有对象A,以及包含对象B和A.equals(B)但A!= B的集合S,并且想要获取对B的引用,则可以调用S.get(A)来获取对B的引用。 B,假设您有一个具有List的get方法的语义的get方法,这与检查S.contains(A)(它会那样)是不同的用例。这甚至不是集合的用例。
–汤姆·特雷桑斯基(Tom Tresansky)
2013年11月1日15:21
#19 楼
是的,请使用HashMap
...,但是以一种特殊的方式:我试图使用HashMap
作为伪Set
的陷阱是Map/Set
的“实际”元素与“候选”元素(即使用的元素)之间可能的混淆测试是否已经存在equal
元素。这远非万无一失,但会使您远离陷阱:class SelfMappingHashMap<V> extends HashMap<V, V>{
@Override
public String toString(){
// otherwise you get lots of "... object1=object1, object2=object2..." stuff
return keySet().toString();
}
@Override
public V get( Object key ){
throw new UnsupportedOperationException( "use tryToGetRealFromCandidate()");
}
@Override
public V put( V key, V value ){
// thorny issue here: if you were indavertently to `put`
// a "candidate instance" with the element already in the `Map/Set`:
// these will obviously be considered equivalent
assert key.equals( value );
return super.put( key, value );
}
public V tryToGetRealFromCandidate( V key ){
return super.get(key);
}
}
然后执行以下操作: />但是,...您现在希望
candidate
以某种方式自毁,除非程序员实际上立即将其放在Map/Set
中...您希望contains
“污染” candidate
,以便对其进行任何使用,除非它加入Map
使其成为“ anthema”。也许您可以使SomeClass
实现新的Taintable
接口。更令人满意的解决方案是GettableSet,如下所示。但是,要使此工作正常,您必须负责
SomeClass
的设计,以使所有构造函数都不可见(或...能够并愿意为其设计和使用包装器类):SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
...
realThing.useInSomeWay()...
}
实现:
public interface NoVisibleConstructor {
// again, this is a "nudge" technique, in the sense that there is no known method of
// making an interface enforce "no visible constructor" in its implementing classes
// - of course when Java finally implements full multiple inheritance some reflection
// technique might be used...
NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet );
};
public interface GettableSet<V extends NoVisibleConstructor> extends Set<V> {
V getGenuineFromImpostor( V impostor ); // see below for naming
}
您的
NoVisibleConstructor
类如下所示: br /> PS具有这样的
NoVisibleConstructor
类的一个技术问题:可能会反对这种类本质上是final
,这可能是不希望的。实际上,您总是可以添加一个无参数的伪protected
构造函数:public class GettableHashSet<V extends NoVisibleConstructor> implements GettableSet<V> {
private Map<V, V> map = new HashMap<V, V>();
@Override
public V getGenuineFromImpostor(V impostor ) {
return map.get( impostor );
}
@Override
public int size() {
return map.size();
}
@Override
public boolean contains(Object o) {
return map.containsKey( o );
}
@Override
public boolean add(V e) {
assert e != null;
V result = map.put( e, e );
return result != null;
}
@Override
public boolean remove(Object o) {
V result = map.remove( o );
return result != null;
}
@Override
public boolean addAll(Collection<? extends V> c) {
// for example:
throw new UnsupportedOperationException();
}
@Override
public void clear() {
map.clear();
}
// implement the other methods from Set ...
}
...至少可以让子类进行编译。然后,您必须考虑是否需要在子类中包括另一个
getOrCreate()
工厂方法。 set),以便您的set成员使用(如果可能的话-再次,使用包装类的范围,该类不在您的控制之下,或者已经具有基类,等等),以最大程度地实现隐藏: class SomeClass implements NoVisibleConstructor {
private SomeClass( Object param1, Object param2 ){
// ...
}
static SomeClass getOrCreate( GettableSet<SomeClass> gettableSet, Object param1, Object param2 ) {
SomeClass candidate = new SomeClass( param1, param2 );
if (gettableSet.contains(candidate)) {
// obviously this then means that the candidate "fails" (or is revealed
// to be an "impostor" if you will). Return the existing element:
return gettableSet.getGenuineFromImpostor(candidate);
}
gettableSet.add( candidate );
return candidate;
}
@Override
public NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet ){
// more elegant implementation-hiding: see below
}
}
...用法非常明显(在
SomeClass
的static
工厂方法内部):protected SomeClass(){
throw new UnsupportedOperationException();
}
评论
不。如果您确实想要一个新的数据结构来管理它,则应该扩展Set接口,例如,接口RetrievableElementsSet
– zakmck
10月7日11:10
@zakmck首先,感谢您实际上对我的回答很感兴趣。重新阅读它,我认为您认为它是“混乱”是正确的。我不太了解您的推理或最终目的(“很快”?),但再次感谢您对此事进行了思考。
–麦克·啮齿动物
10月7日18:24
我的意思是“快速”。直接使用Map
– zakmck
10月8日,0:11
#20 楼
哈希码的约定清楚地表明:“如果根据Object方法,两个对象相等,则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果” >不是所有字段。所以两个被认为相等的Foo对象实际上可以具有不同的值,这就是为什么我不能只使用foo。”
是错误的,您正在违约。如果我们看一下Set接口的“ contains”方法,就会发现:
如果此集合包含指定的元素,则返回true。正式地,More
,当且仅当该集合包含元素o <= null时,才返回true。 e == null:o.equals(e)
要实现所需的功能,可以在定义键的地方使用Map并使用定义对象的键的方式存储元素彼此不同或相等。
#21 楼
如果您拥有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);
}
}
#22 楼
尝试使用数组:ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
#23 楼
可能解决这种情况的快速帮助方法:<T> T onlyItem(Collection<T> items) {
if (items.size() != 1)
throw new IllegalArgumentException("Collection must have single item; instead it has " + items.size());
return items.iterator().next();
}
评论
奇怪的是,这个答案获得了如此之多的好评,因为它没有回答问题,甚至没有试图以任何方式解决它。
–大卫·康拉德(David Conrad)
16年11月1日在12:58
#24 楼
以下可能是一种方法 SharedPreferences se_get = getSharedPreferences("points",MODE_PRIVATE);
Set<String> main = se_get.getStringSet("mydata",null);
for(int jk = 0 ; jk < main.size();jk++)
{
Log.i("data",String.valueOf(main.toArray()[jk]));
}
评论
这篇文章已经被广泛讨论,并提出了很好的答案。但是,如果您只是在寻找有序集合,只需使用SortedSet及其基于地图的实现(例如TreeSet允许访问first())。对于上面描述的完全相同的情况,我也缺少该方法。 Objective-C(NSSet)具有这种方法。它称为成员,它返回集合中的对象,该对象将“等于”与成员方法的参数进行比较(当然,它可能是一个不同的对象,并且也具有不同的属性,该相等可能不会检查)。