Set集合HashSet学习(Set集合学习一)

本文详细介绍了Set集合的概念、特点及其实现类HashSet的工作原理。包括哈希表存储机制、如何保证元素唯一性、自定义对象存储注意事项以及去除ArrayList重复元素的实践案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Set集合HashSet学习(Set集合学习一)

 

Set:元素不可以重复,是无序。

1、set接口的实现类

       Set接口中的方法和Collection一致。

       |--HashSet: 内部数据结构是哈希表 ,是不同步的。

|-- LinkedHashSet:内部数据结构是哈希表和链表有序的

|--TreeSet: 内部数据结构是二叉树,可以对Set集合中的元素进行排序。是不同步的。

2、HashSet: 内部数据结构是哈希表 ,是不同步的

2.1HashSet哈希表说明


哈希表中,存储时是根据元素的特点,来决定元素在数组中的位置,这个过程是通过哈希算法来实现的。

用哈希表存储的好处:查询非常快!不需要遍历集合中的每个元素,只需要拿到该元素的信息,然后利用哈希算法,就可以找到该元素所在的位置

2.2HashSet如何保证该集合的元素唯一性呢?

哈希表确定元素是否相同

是通过对象的hashCodeequals方法来完成对象唯一性的。

if(this.hashCode()== obj.hashCode()&& this.equals(obj))

【1】如果对象的hashCode值不同(对象的hashCode方法),那么不用判断equals方法,就直接存储到哈希表中。

【2】如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。

【3】如果equals方法为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储

注意:如果哈希值不同,是不需要判断equals

其中,hashCode方法确定位置是否相同;equals方法确定对象内容是否相同。

2.3HashSet集合使用注意

(1)java类库中已有的对象

使用java类库中已有的对象时,看看它是否有覆盖Object对象的hashCode方法和equals方法,如果没有,则需要自己去覆盖;如果有,则不用。

举例说明:

String对象覆盖了Object的hashCode方法和equals方法,所以使用时,直接拿来用即可,不用考虑对象的覆盖情况。

(2)使用自定义对象

使用自定义对象,需要自己手动覆盖hashCode方法和equals方法。

举例说明:

我自己定义了一个people类,里面有name和age属性,我希望这个实例化后的people对象存入HashSet中,能通过name和age两个属性来判断,这个对象是否已经在HashSet集合中存储过了,为了确保存取的唯一性,我需要覆盖hashCode方法和equals方法。

(3)问题与解答

问题:项目开发中,使用自定义对象,使用hashSet,如何保证唯一性?

答案:在自定义对象中,添加两个方法,一个hashCode()方法,一个equals方法,里面书写自己的判断逻辑,达到确保存放对象唯一性的目的!

记住:

一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。建立对象判断是否相同的依据

(4)hashCode方法,equals方法注意

hashCode方法:里面书写生成位置的逻辑,可以给现有条件下,自己生成的的数值,再去乘以一些加到奇数,增加随机性。

equals方法:

【1】进行比较内容

【2】添加健壮性判断:<1>可以看看两个比较的对象的地址是否相同,如果相同,则不进行存入;<2>看看比较的两个对象的类型是否相同,如果类型不同,则直接抛出异常等!!!

(5)自定义对象people使用HashSet集合

【1】People对象(覆盖hashCode方法和equals方法)
package list_set;
 
public class People {
 
    private String name;
    private int age;
    public People(String name, int age) {
       super();
       this.name = name;
       this.age = age;
    }
    public String getName() {
       return name;
    }
    public void setName(String name) {
       this.name = name;
    }
    public int getAge() {
       return age;
    }
    public void setAge(int age) {
       this.age = age;
    }
 
    @Override
    public int hashCode() {
       //name的哈希值+age的值乘以某个奇数
       return this.name.hashCode()+age*37;
    }
 
    @Override
    public boolean equals(Object obj) {
 
       People p=(People)obj;
      
       //比较姓名和年龄是否相等
       if(this.getName().equals(p.getName())&&this.getAge()==p.getAge()){
           return true;
       }
       return false;
    }
   
   
}
【2】HashSet集合使用

package list_set;
 
import java.util.HashSet;
import java.util.Iterator;
 
public class PeopleSet {
 
    public static void main(String[] args) {
 
       HashSet set=new HashSet();
       set.add(new People("张三",23));
      
       set.add(new People("张三",23));//去除重复,保证唯一性   自定义hashcode方法和equals方法
      
       set.add(new People("李四",24));
       set.add(new People("王五",24));
       set.add(new People("赵六",25));
       set.add(new People("你好",26));
      
       //遍历
       for(Iterator it=set.iterator();it.hasNext();){
           People p=(People)it.next();
           System.out.println(p.getName()+":"+p.getAge());
       }
      
    }
 
}

结果:(保证了存入对象的唯一性)

赵六:25

你好:26

张三:23

李四:24

王五:24

 

2.4 实现功能:去除ArrayList中的重复元素

(1)字符串对象实现

package list_set;
 
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
 
/*
 * 去除ArrayList中的重复元素
 * 元素是:String对象(jdk中已有对象)
 */
public class Remove_repeat1 {
 
    public static void main(String[] args) {
 
       ArrayList ar = new ArrayList();
 
       ar.add("张三");
       ar.add("张三");
       ar.add("李四");// 去除重复
       ar.add("王五");
       ar.add("李白");
 
       ar = noReapeat(ar);// 去重操作
       // 遍历
       for (Iterator it = ar.iterator(); it.hasNext();) {
 
           System.out.println(it.next());
       }
    }
 
    /*
     * 去重操作
     * 该容器为ar
     *  1、创建临时容器temp
     *  2、遍历容器ar,一个个进行判断,看看该元素是否在temp中;
     *     如果在temp中,则不存;否则,进行存储
     */
    public static ArrayList noReapeat(ArrayListar) {
 
       //临时容器temp
       ArrayList temp=new ArrayList();
       //遍历容器ar
       for (Iterator it = ar.iterator(); it.hasNext();) {
 
           Object obj=it.next();//当前元素
           //temp中不存在当前元素,进行存储
          
           /*
            * 成功了!
            * 查看源码:发现ArrayList中的contains方法最终的判断依据是:对象的equals方法
            *    就是说,如果对象有自己的equals方法,就调用自己的进行判断;
            *    如果没有,则调用Object对象的equals方法进行判断。
            * 此处:String对象,自己有equals方法,覆盖了Object的方法,这里equals方法比较的是两个字符串对象
            *     的内容,而不是两个对象的地址,因此达到了比较的目的。
            * 因此,达到了去重的目的。
            */
                     
           if(!temp.contains(obj)){
             
              temp.add(obj);
           }
       }
       return temp;//返回临时容器
    }
 
}

(2)自定义对象实现

【1】自定义对象People
package list_set;
 
public class People {
 
    private String name;
    private int age;
   
   
    public People(String name, int age) {
       super();
       this.name = name;
       this.age = age;
    }
   
    public String getName() {
       return name;
    }
    public void setName(String name) {
       this.name = name;
    }
    public int getAge() {
       return age;
    }
    public void setAge(int age) {
       this.age = age;
    }
    @Override
    public boolean equals(Object obj) {
 
       People p=(People)obj;
      
       //比较姓名和年龄是否相等
       if(this.getName().equals(p.getName())&&this.getAge()==p.getAge())
       {
           return true;
       }
       return false;
    }
   
   
}
【2】去重代码测试类

package list_set;
 
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
 
/*
 * 去除ArrayList中的重复元素
 * 元素是:自定义对象
 */
public class Remove_repeat {
 
    public static void main(String[] args) {
 
       ArrayList ar = new ArrayList();
 
//     AbstractList abs=new ArrayList();
//     abs.add(1);
      
       ar.add(new People("张三", 23));
       ar.add(new People("张三", 23));// 重复,如何去除???
       ar.add(new People("李四", 24));
       ar.add(new People("王五", 24));
       ar.add(new People("赵六", 25));
       ar.add(new People("你好", 26));
 
       ar = noReapeat(ar);// 去重操作
 
       for (Iterator it = ar.iterator(); it.hasNext();) {
           People p = (People) it.next();
           System.out.println(p.getName() + ":" + p.getAge());
 
       }
      
    }
 
    /*
     * 去重操作思路:建立一个临时容器temp,把容器ar去重后的数据放到临时容器中,最终将集合指向这个临时容器即可
     *  做法:
     * 1、将容器ar中的元素,一个个放入到临时容器temp中,并首先在temp中是否存在该元素,使用contains方法判断
     * 2、如果存在,则不进行存储;如果不存在,则存储到temp中
     */
    public static ArrayList noReapeat(ArrayListar) {
 
       //临时容器temp
       ArrayList temp=new ArrayList();
       for (Iterator it = ar.iterator(); it.hasNext();) {
           People p = (People) it.next();
           //发现,这样判断后,仍旧没有什么效果,还是达不到去重的效果!为什么?
          
           /*思考:难道contains方法失效???
            * 查询ArrayList的源码,查看contains方法的实现,
            * 发现contains方法,最终调用的是:Object对象的equals方法进行比较的,
            * 原因:此处的Object对象是:People,因为People没有实现equals方法,
            * 所以最终实际上调用的就是Object对象的equals方法
            * 即:可以知道,这个Object对象的方法,比较的是两个对象的地址是否相同。
            * 因此,没有达到去重的目的,因为任何的对象,创建的地址都是不同的!
            *
            * 推论:我们想要达到去重的效果,就得在自己的自定义对象中People,
            * 覆盖Object对象的equals方法,加入自己的自定义内容(自己的判断逻辑),达到去重的目的。
            *
            * 结果:证实这一推论是成立的。达到了去重的效果
            *
            * 比较HashSet
            * ArrayList  vs  HashSet
            * HashSet 需要覆盖 hashCode方法和equals方法达到去重的目的
            * ArrayList 需要覆盖equals方法达到去重的目的。
            *
            * 发现:
            * ArrayList中的equals方法:如果对象有自己的equals方法,则调用自己所写;
            *                          如果没有,调用的是Object的equals方法
            */
          
           if(!temp.contains(p)){
              temp.add(p);//不存在,进行添加
           }
       }
       return temp;
    }
 
}

(3)字符串对象实现和自定义对象实现比较分析

【1】字符串对象实现分析:

在判断元素是否存在于集合中的时候,只需用集合的contains方法判断即可。

分析:

查看源码:发现ArrayList中的contains方法最终的判断依据是:对象的equals方法。

就是说,如果对象有自己的equals方法,就调用自己的进行判断;

如果没有,则调用Object对象的equals方法进行判断。

此处:String对象,自己有equals方法,覆盖了Object的方法,这里equals方法比较的是两个字符串对象的内容,而不是两个对象的地址,因此达到了比较的目的。

 

【2】自定义对象实现分析:

在判断元素是否存在于集合中的时候,不仅需用集合的contains方法判断;而且我们还要给对象写个equals方法。

假设自定义对象是:People

分析:

分析ArrayList源码,发现contains方法,最终调用的是:Object对象的equals方法进行比较的!

原因:此处的Object对象是:People,因为People没有实现equals方法,所以最终实际上调用的就是Object对象的equals方法

因此,可以知道,这个Object对象的方法,比较的是两个对象的地址是否相同。

所以,此时,利用contains方法(底层实现:equals())去判断两个对象是否相同,是没有意义的,因为任何的对象,创建的地址都是不同的!

推论:我们想要判断,就得在自己的自定义对象中People,

覆盖Object对象的equals方法,加入自己的自定义内容(自己的判断逻辑)。

(3)HashSet  和 ArrayList比较

结论:

HashSetArrayList的数据结构不同,所以,对于一些情况,判断的依据会有所不同!

主要体现在:

1)查找,判断元素是否相同 使用的方法不同。

2)删除一个元素使用该方法不同

ArrayList集合只是用equals方法,HashSet同时使用hashCodeequals方法

 

1】判断元素是否相同(contains方法)原理:

ArrayList集合:判断元素是否相同,仅仅使用元素的equals方法

HashSet集合:判断元素是否相同,同时使用hashCode方法和equals方法【先:hashCode方法   后:equals方法】

 

结论:

<1>我们使用ArrayList时,想要保证ArrayList集合中的元素是唯一的,需要在ArrayList中存储的对象中覆盖对象的equals方法(自定义对象)

<2>我们使用HashSet集合时,想要保证HashSet集合中的元素是唯一的,需要在HashSet中存储的对象中覆盖对象的equals方法和hashCode方法。(自定义对象)

备注:如果集合中存储的对象不是自定义对象,而是jdk中已有的对象,而且该对象中已经覆盖了equals方法或者equals方法和hashCode方法,我们在用ArrayList集合和HashSet集合时就无需考虑给对象覆盖方法的事,直接拿来用即可。

 

2】删除一个元素原理(remove方法)

ArrayList集合:然后原理利用equals方法查找,把集合中的该东西删除掉。

HashSet集合:判断元素是否相同,同时使用hashCode方法和equals方法查找,然后删除掉。

 

3、LinkedHashSet(哈希表和链表实现)--有序的

特殊:有序的

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表

此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。【保证了元素的有序性,元素插入顺序和迭代顺序相同】

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值