关于集合,你搞明白了吗?

1、当向集合set中增加对象时,首先集合计算要增加对象的hashCode码,根据该值来得到一个位置来存放当前的对象,当在该位置没有一个对象存在的话,那么集合set认为该对象在集合中不存在,直接增加进去。如果在该位置有一个对象的话,接着将准备增加到集合中的对象与该位置上的对象进行equals方法比较,如果该equals方法返回false,那么集合认为集合中不存在该对象,再进行一次散列,将该对象放到散列后计算出的新地址里,如果equals方法返回true,那么集合认为集合中已经存在该对象了,不会再将该对象增加到集合中了。

2、当重写eqals方法时,必须要重写hashCode方法,如果一个类的两个对象,使用equals方法比较时,结果为true,那么该两个对象必须具有相同的hashCode

Object的hashCode()方法中有这样的说明:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

意味着在比较对象的时候需要重写hashCode()和equals()方法,并且要保证两者返回的是相同结果,也就是说:重写了两个方法后,equals()返回的是true时,hashCode()也必须返回true;equals()返回的是false时,hashCode()也必须返回false

Object的hashCode()计算的是内存地址,只有内存地址相同时,两个Object的hashCode()方法返回相同的值。

 示例代码:

java 代码
  1. package com.test;   
  2.   
  3. import java.util.HashSet;   
  4. import java.util.Iterator;   
  5. import java.util.Set;   
  6.   
  7. public class SetTest {   
  8.   
  9.     public static void main(String[] args) {   
  10.         Set set = new HashSet();   
  11.            
  12.         set.add(new String("abc"));   
  13.         set.add(new String("xyz"));   
  14.         set.add(new String("abc"));   
  15.            
  16.         for(Iterator iter = set.iterator();iter.hasNext();)   
  17.         {   
  18.             System.out.println(iter.next());   
  19.         }   
  20.            
  21.         Set  set2 = new HashSet ();   
  22.            
  23.         set2.add(new People("zhangsan"));   
  24.         set2.add(new People("lisi"));   
  25.         set2.add(new People("zhangsan"));   
  26.            
  27.         for(Iterator  iter = set2.iterator();iter.hasNext();)   
  28.         {   
  29.             System.out.println(iter.next().getName());   
  30.         }   
  31.     }   
  32.   
  33. }   
  34.   
  35. class People   
  36. {   
  37.     private String name;   
  38.     public People(String name)   
  39.     {   
  40.         this.name = name;   
  41.     }   
  42.     public String getName()   
  43.     {   
  44.         return this.name;   
  45.     }   
  46.        
  47.     public boolean equals(Object obj)   
  48.     {   
  49.         if(null != obj)   
  50.         {   
  51.             if(this == obj)   
  52.             {   
  53.                 return true;   
  54.             }   
  55.             if(obj instanceof People)   
  56.             {   
  57.                 People people = (People)obj;   
  58.                 if((this.name).equals(people.name))   
  59.                 {   
  60.                     return true;   
  61.                 }   
  62.             }   
  63.         }   
  64.            
  65.         return false;   
  66.     }   
  67.        
  68.     public int hashCode()   
  69.     {   
  70.         return this.name.hashCode();   
  71.     }   
  72. }   

 

输出结果:

xyz
abc
zhangsan
lisi

原因是String类重写了hashCode和equals方法,使得当String对象的内容相同时,都返回true

而在程序代码People中我们也重写了Object的hashCode和equals方法,使得当People对象的name属性值内容相同时都返回true

<think>嗯,用户现在问的问题挺有意思的。他们之前已经了解了深拷贝的概念,以及如何通过手动编写构造方法或者使用clone方法来实现深拷贝。现在用户提到第一个方法不就是套个构造就解决了吗,后面的方法看起来太抽象了,为什么要那么复杂呢? 首先,我需要理解用户的具体困惑点在哪里。用户可能觉得,只要在构造方法里手动复制所有字段,包括引用类型的字段,比如Address,就能轻松实现深拷贝,不需要用clone方法或者序列化那些更复杂的方式。那为什么还要介绍后面的方法呢? 用户可能的问题在于,他们认为手动构造方法已经足够简单有效,后面的方法反而增加了复杂度,没有必要。这时候我需要解释为什么在某些情况下,手动构造方法可能不够,或者有其他更好的方法。 接下来,我得考虑用户的背景。可能他们是一个刚开始学习编程的新手,或者对深拷贝的概念还不是很熟悉,所以更倾向于简单直接的解决方案,而对更抽象或通用的方法感到困惑。 然后,我需要分析用户的问题核心。用户提到的“套个构造然后自己点出来改值”可能指的是在构造方法中逐一复制每个字段的值,包括新建引用类型对象。而他们觉得后面的方法,比如使用clone或者序列化,显得过于复杂和抽象,不如手动构造直接。 这时候,我需要指出手动构造方法的优缺点,以及为什么其他方法在某些情况下更优。比如,手动构造方法在类结构简单时确实有效,但当类层次较深或者有多个引用字段时,手动编写构造方法会变得繁琐且容易出错。此外,当类结构发生变化时,需要维护所有的构造方法,增加了维护成本。 同时,用户可能没有考虑到代码的可维护性和扩展性。如果每个类都需要手动编写深拷贝构造方法,当类有多个引用字段或者嵌套层次很深时,代码量会急剧增加,容易遗漏某些字段,导致浅拷贝的问题依然存在。 另外,用户可能对clone方法或序列化方法的机制不太了解,觉得这些方法抽象难。需要解释这些方法的优势,比如自动处理所有字段的复制,减少手动错误,提高代码的复用性。 可能用户还有一个隐藏的需求,就是寻找一种更高效、更省事的方式来实现深拷贝,而不需要每次都手动编写大量代码。这时候,介绍一些工具类或者库,比如Apache Commons Lang的SerializationUtils,或者使用JSON序列化/反序列化的方式,可能更符合他们的实际需求。 此外,用户可能在实践中遇到了手动构造方法带来的问题,比如维护困难或者出错,所以才会对更抽象的方法产生疑问。这时候需要举例说明,当类结构复杂时,手动方法的不足,以及自动方法的优势。 最后,要确保回答清晰易,用具体的例子说明不同方法的适用场景,以及各自的优缺点。让用户明白,选择哪种方法取决于具体情况,没有绝对的好坏,只有适合与否。</think>你提到的思路非常直接,确实是解决深拷贝问题的一种有效方式。但实际场景中,问题的复杂度可能远超简单的手动赋值。下面我会详细解释你的思路的合理性、适用场景,以及为何在某些情况下需要更“抽象”的解决方案。 --- ### **你的思路:手动构造 + 赋值** 假设有一个 `Person` 类,包含 `name`(基本类型)和 `address`(引用类型)字段: ```java class Person { String name; Address address; // 手动深拷贝构造方法 public Person(Person original) { this.name = original.name; this.address = new Address(original.address.city); // 为引用类型字段创建新对象 } } ``` 通过手动编写构造方法,为每个引用类型字段显式创建新对象,这确实是实现深拷贝的一种方式。 **优点**: - 简单直观,代码可控。 - 对简单对象(字段少、层级浅)非常有效。 --- ### **为什么需要更“抽象”的方法?** #### **场景 1:对象结构复杂化** 假设 `Person` 类新增一个引用类型字段 `Job`,且 `Job` 内部又包含其他引用类型字段(如 `Company`): ```java class Person { String name; Address address; Job job; // 新增字段 } class Job { String title; Company company; // 嵌套引用类型 } class Company { String name; } ``` 此时,手动深拷贝的构造方法需要层层递归赋值: ```java public Person(Person original) { this.name = original.name; this.address = new Address(original.address.city); this.job = new Job(original.job.title, new Company(original.job.company.name)); // 层层手动创建 } ``` **问题**: - **代码冗余**:每新增一个字段,就要修改构造方法。 - **易遗漏字段**:如果忘记处理某个引用字段(如 `Company`),会导致浅拷贝漏洞。 - **可维护性差**:对象层级越深,代码越臃肿。 --- #### **场景 2:动态对象(如集合、数组)** 如果对象包含集合或动态结构(如 `List<Friend>`): ```java class Person { String name; List<Friend> friends; // 集合类型的引用字段 } class Friend { String name; } ``` 手动深拷贝需要遍历集合并逐个复制元素: ```java public Person(Person original) { this.name = original.name; this.friends = new ArrayList<>(); for (Friend friend : original.friends) { this.friends.add(new Friend(friend.name)); // 手动遍历并复制集合 } } ``` **问题**: - **通用性差**:每个集合类型(如 `List`、`Map`)都需要单独处理。 - **性能问题**:大规模集合的遍历复制可能低效。 --- ### **为什么需要“抽象”的深拷贝方法?** #### **方法 1:递归 `clone()`** 通过实现 `Cloneable` 接口并递归调用 `clone()`,可以避免手动逐层赋值: ```java class Person implements Cloneable { String name; Address address; @Override public Person clone() { try { Person cloned = (Person) super.clone(); cloned.address = address.clone(); // 依赖 Address 自身的 clone 方法 return cloned; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } } class Address implements Cloneable { String city; @Override public Address clone() { try { return (Address) super.clone(); // 自动复制所有字段(包括引用类型) } catch (CloneNotSupportedException e) { throw new AssertionError(); } } } ``` **优点**: - **职责分离**:每个类自行实现 `clone()`,调用者无需关心内部结构。 - **可扩展性**:新增字段时,只需修改当前类的 `clone()` 方法。 --- #### **方法 2:序列化/反序列化** 通过序列化工具(如 `SerializationUtils`)实现深拷贝: ```java import org.apache.commons.lang3.SerializationUtils; Person a = new Person("Alice", new Address("北京")); Person b = SerializationUtils.clone(a); // 一行代码完成深拷贝 ``` **优点**: - **通用性强**:无需手动编写任何拷贝逻辑,适用于任何可序列化对象。 - **安全性高**:自动处理所有引用类型的递归复制,避免遗漏。 --- ### **你的思路 vs 抽象方法** | **场景** | **手动构造方法** | **递归 `clone()` 或序列化** | |------------------------|-----------------------------------|------------------------------------| | **简单对象** | 直接有效,代码少 | 可能过度设计 | | **复杂对象** | 代码冗余,易出错 | 自动递归复制,省力 | | **动态结构(集合)** | 需手动遍历,代码复杂 | 自动处理集合内部元素 | | **可维护性** | 新增字段需修改构造方法 | 新增字段只需修改当前类的 `clone()` | | **开发效率** | 适合一次性需求 | 适合长期维护的代码 | --- ### **总结** - **手动构造方法**的合理性: - 在对象结构简单、字段固定的场景下,手动赋值是最直接的解决方案。 - **为何需要抽象方法**: - 当对象结构复杂、包含动态字段(如集合)或需要长期维护时,抽象方法(`clone()`、序列化)能显著提升代码的健壮性和可维护性。 - **核心原则**: - **根据场景选择工具**:简单场景用构造方法,复杂场景用抽象方法。 - **避免重复劳动**:抽象方法的本质是“将深拷贝的逻辑封装在对象自身中”,减少调用者的心智负担。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值