【问题标题】:Java: How to be sure to store unique arrays based on its values on a ListJava:如何确保根据 List 上的值存储唯一数组
【发布时间】:2009-10-20 15:45:56
【问题描述】:

我有许多 Object[] 的一维数组(如果有帮助,这些对象是原始类型)

我想将这些数组存储在一个列表中,但只存储其内容与其他数组不同的数组。

我的第一个近似值是遍历存储在 Set 中的数组,将 Arrays.hashCode(array) 的值存储在所需的列表中,如果该值未包含在集合中。

但后来我意识到两个具有不同内容的数组可以产生相同的哈希码(我希望不经常)

谁能帮忙?

我可以期待非常频繁的哈希码冲突(不同内容的相同哈希码)吗?

【问题讨论】:

  • 数组中元素的顺序重要吗?您认为数组 {1,2,3} 和 {3,2,1} 是否重复?
  • 是的,它们对我来说不是同一个数组
  • +1 表示有很多答案的问题 :-)
  • 假设您有数组 {1,2,3}、{a,b,c} 和 {1,2,3},您是否期望答案是“{1,2,3 }、{a,b,c}”还是只是“{a,b,c}”?也就是说,结果中是否包含重复数组的一份副本?

标签: java arrays hashcode


【解决方案1】:

听起来您需要一个 LinkedHashSet(在保持唯一性的同时保留插入顺序),然​​后将您的数组包装在一个对象中,该对象以对您的数组有意义的方式实现 hashcode 和 equal。第一个近似值可能只是 Arrays.asList() 方法,但您在问题中声明您在 Object[] 数组中使用原语。要么您依赖自动装箱,要么您实际上没有使用 Object[] 数组,而是根据需要使用 int[]、long[]、float[]。 Arrays.asList() 无法与这些类型一起正常工作。

编辑:根据评论的要求,这里是包装类的代码:

  public class ArrayWrapper { 
       private Object[]array; 
       public ArrayWrapper(Object[] array) { this.array = array; } 
       public Object[] getArray() { 
                 Object[] newArray=new Object[array.length]; 
                 System.arraycopy(array,0,newArray,0,array.length); 
                  return newArray; 
       } 
       public int hashCode() { return Arrays.hashCode(array); } 
       public boolean equals(Object obj) { 
              boolean b=false;
              if(obj instanceof ArrayWrapper){ 
                     b=Arrays.equals(this.array,((ArrayWrapper)obj).getArray()); 
              } 
              return b; 
       } 
 }

【讨论】:

  • 我终于像你建议的那样使用了一个包装类,我在下面的评论中发布了代码,你能编辑你的答案以包含代码吗?
  • 公共类 ArrayWrapper { private Object[]array;公共 ArrayWrapper(对象 [] 数组) { this.array = 数组; } public Object[] getArray() { Object[] newArray=new Object[array.length]; System.arraycopy(array,0,newArray,0,array.length);返回新数组; } public int hashCode() { return Arrays.hashCode(array); } 公共布尔等于(对象 obj){ 布尔 b=false; if(obj instanceof ArrayWrapper){ b=Arrays.equals(this.array,((ArrayWrapper)obj).getArray()); } 返回 b; } }
  • getArray 方法注意事项:我返回数组的副本以使 ArrayWrapper 的内容和哈希码不变。数组中的内容不应在应用程序中更改
【解决方案2】:

问题是您将拥有 arrayX 和 arrayY,两者都具有内容 [a,b,c] 但 Set 不将它们视为平等? [a,b,c] 和 [c,a,b] 是否相等?

我会说定义一个比较器,它为数组定义“相等”,正是您需要它定义的方式,然后将每个数组插入到使用您创建的自定义比较器的 Set 中。

【讨论】:

    【解决方案3】:

    如果哈希码相同,则只需进一步检查其详细信息。

    【讨论】:

    • 有趣,您的意思是使用哈希码/数组列表的哈希图。当我找到一个现有的 hascode 时,我遍历调用 Arrays.equals(array1,array2) 的数组列表,如果我没有找到重复项,请将其存储...它可以工作,明天我尝试并接受答案
    【解决方案4】:

    试试这样的:

    编辑

    下面的运行和工作代码:

    bash-3.2$ cat ArraysTest.java 
    import java.util.*;
    public class ArraysTest {
        public static void main( String [] args ) {
            Set<Integer[]> set = new TreeSet<Integer[]>( new Comparator<Integer[]>() {
                public int compare( Integer[] one, Integer[] two ) {
                    if( Arrays.equals( one, two ) )  {
                        return 0;
                    }
                    return Arrays.hashCode( one ) - Arrays.hashCode( two );
                }
                public boolean equals( Object o ){ return false; }
            });
    
            set.add( new Integer[]{1,2,3});
            set.add( new Integer[]{1,2,3});
            set.add( new Integer[]{3,2,1});
    
            for( Integer[] i : set ) {
                System.out.println( Arrays.asList( i ) );
            }
    
        }
    }
    
    bash-3.2$ javac ArraysTest.java  
    bash-3.2$ java ArraysTest
    [1, 2, 3]
    [3, 2, 1]
    bash-3.2$ 
    

    你需要做一些工作才能让它工作,这只是一个示例,不是实际运行的代码。

    您知道 Set 只接受一个元素,并且使用自定义比较器创建 TreeSet 可以让您告诉 set 什么对您来说是相等的。

    Arrays.equals()方法描述:

    ..如果两个数组包含相同顺序的相同元素,则它们相等...

    【讨论】:

    • 当我使用你的代码时,我得到了这个错误.....类型比较器不是通用的;它不能用参数 参数化可能是什么原因....?
    【解决方案5】:

    以下假设您认为数组 {1,2,3} 和 {3,2,1} 不重复。

    不要将数组的哈希码存储到 Set,将整个列表存储到 Set。

    将您的数组转换为Lists。列表具有一致的 equalshashCode 方法。 如果两个列表包含相同顺序的相同元素,则定义为相等,并且列表的hashCode将与equals方法保持一致。

      List<Object> list = Arrays.asList(array);
    

    这是整个算法。 (未经测试的代码,但应该可以工作)。

    Set<List<Object>> findUniqueLists(List<List<Object>> allLists) {
       Set<List<Object>> uniqueSet = new LinkedHashSet<List<Object>>();
       uniqueSet.addAll(allLists);
    
       Set<List<Object>> processedSet = new LinkedHashSet<List<Object>>();
    
       for(List<Object> list : allLists) {
           if(processedSet.contains(list)) {
               // duplicate found!
               uniqueSet.remove(list);
           } else {
               // no duplicate
               processedSet.add(list)
           }
        }
        return uniqueSet;
    }
    

    【讨论】:

    • 他将“不同顺序的相同元素”定义为仍然相等,因此“如果两个列表包含相同顺序的相同元素,则定义为相等”是不够的。
    【解决方案6】:

    为了有效地比较,有时使用两步法:

    1. hashCode 丢弃了许多潜在的匹配项
    2. 如果两个 hashCode 相等,则测试对象本身是否相等(取决于它们的方法 equals

    关于您的Object[] 属于原始类型,请记住以下几点:
    要将原始类型添​​加到 Object[] 中,它将始终装箱/取消装箱
    所以你并没有真正将原始类型作为数组的内容。

    为了保留原始类型,数组本身必须是原始类型,例如int[]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-16
      • 2020-04-15
      相关资源
      最近更新 更多