集合中的hashCode和equals方法

25 篇文章 0 订阅
当处理包含重复数字的全排列问题时,为避免结果重复,可以使用Set来存储。Set在添加元素时依赖于对象的hashCode和equals方法。对于Integer类型,其已重写这两个方法,确保相同整数值的Integer对象被视为相等。因此,使用ArrayList存储Integer数组并添加到Set中能有效去重。而对于自定义类,若未重写hashCode和equals,使用Set时可能无法正确去重。同样,String类也重写了这两个方法,适合在字符串排列问题中用于去重,但StringBuilder则未重写,需谨慎使用。
摘要由CSDN通过智能技术生成

在做全排列的题目的时候,有一种是原数组有重复数字,这样会造成用回溯后得到的结果有重复数组,为了去重,可以用set存放结果。

47. 全排列 II

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

————————————————————————————————————————————————————

set添加元素时,第一比较hashCode,第二比较equals方法

如果用数组存放每个结果的话,数组是没有重写这两个方法的

也就是 A={1,2,3},B={1,2,3}

A.equals(B) 是false

可以用Arrays.equals(A,B),比较,这样就是true。

 

如果用集合存放结果的话,比如用ArrayList,需要考虑下面几个问题:

1 首先查看ArrayList的hashCode()和equals方法,看下源码:

   public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

也就是说这个数组的hashCode是和每个对象的hashCode有关的。

   public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }

equals方法也是和存储的数据类型的equals方法相关的。

所以List的比较,是要看存储元素的类型的,有没有重写hashCode和equals

 

以这个题目来说,存放的是Integer元素,可以看源码,Integer重写了hashCode和equals

 public static int hashCode(int value) {
        return value;
    }


    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

也就是只要对应的int值相等,hashCode和equals都相等。

(但是注意地址不一定相等,只有[-128,127]会使用缓存)

所以这个题目,我们可以放心使用ArrayList,然后把结果添加到set,就可以放心去重。

如果你自定义了一个类,没有重写hashCode和equals方法,那么你添加到ArrayList后,这两个ArrayList一定不相等。

这题代码如下:

class Solution {
    HashSet<LinkedList<Integer>> set = new HashSet<>();
    LinkedList<Integer> temp = new LinkedList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        boolean[] visited = new boolean[nums.length];
        permute(nums,0,visited);
        return new ArrayList(set);
          
    }
      public void permute(int[] nums,int k,boolean[] visited) {
          if(k==nums.length) {
              set.add(new LinkedList(temp));
              return;
          }
          for(int i =0;i<nums.length;i++) {
              if(!visited[i]) {
               temp.add(nums[i]);
               visited[i] = true;
               permute(nums,k+1,visited);
               visited[i] = false;
               temp.removeLast(); 
              }
          }      
 }
}

这个题还有个姐妹题:

面试题38. 字符串的排列

输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

 

示例:

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

 

同理,String重写了hashCode和equals,比较是否相等,都只和本身元素内容有关而与地址无关,可以放心地使用set去重。

(注意StringBuilder并没有重写hashCode和equals方法,使用请小心,如此题是无法用set去重的)

 public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
   public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值