【问题标题】:Check if an array exists in a HashSet<int[]>检查 HashSet<int[]> 中是否存在数组
【发布时间】:2021-04-03 20:54:51
【问题描述】:

如何检查HashSet中是否存在数组?

例如:

int[] a = new int[]{0, 0};

HashSet<int[]> set = new HashSet<>();
set.add(a);

然后:

int[] b = new int[]{0, 0};

set.contains(b); // ===> true

【问题讨论】:

    标签: java arrays collections contains hashset


    【解决方案1】:

    使用数组

        int[] a = new int[] { 0, 0 };
        HashSet<int[]> set = new HashSet<>();
        set.add(a);
    
        int[] b = new int[] { 0, 0 };
        
        boolean contains = set.stream().anyMatch(c -> Arrays.equals(c, b));
        
        System.out.println("Contains? " + contains);
    

    输出:

    包含?真的

    虽然它没有利用HashSet 的快速查找。如 cmets 中所述,这是不可能的,因为数组的 equalshashCode 不认为包含相同顺序的相同数字的数组是相等的。一个数组只被认为等于它自己。因此,我们需要对集合进行线性搜索,以找到包含相同数字的数组(如果有的话)。我正在为此使用流管道。您也可以使用循环。

    更快:使用列表

    要利用HashSet 中的快速查找,您可以使用列表而不是数组:

        List<Integer> a = List.of(0, 0);
        HashSet<List<Integer>> set = new HashSet<>();
        set.add(a);
    
        List<Integer> b = List.of(0, 0);
        
        System.out.println("Contains? " + set.contains(b));
    

    包含?真的

    不过,List&lt;Integer&gt; 方法会占用空间,因为它存储的是 Integer 对象而不是 int 原语,后者通常会占用更多空间。

    既节省空间又节省时间:开发自定义类

    如果上述方法仍然不够有效——这对于绝大多数目的来说都是如此——你可以使用你自己的类来获取数字:

    public class IntArray {
    
        int[] elements;
        
        public IntArray(int... elements) {
            // Make a defensive copy to shield from subsequent modifications of the original array
            this.elements = Arrays.copyOf(elements, elements.length);
        }
    
        @Override
        public int hashCode() {
            return Arrays.hashCode(elements);
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            IntArray other = (IntArray) obj;
            return Arrays.equals(elements, other.elements);
        }
    
    }
    

    这将允许:

        IntArray a = new IntArray(0, 0);
        HashSet<IntArray> set = new HashSet<>();
        set.add(a);
    
        IntArray b = new IntArray(0, 0);
        
        System.out.println("Contains? " + set.contains(b));
    

    包含?真的

    现在我们有了原来int数组方法的空间效率,差不多,和hashCode()的时间效率。

    更多选项(谢谢,蓬松)

    作为 cmets 中的蓬松笔记,还有更多选择,您可能想自己研究一些。我在这里引用 cmets:

    此外,如果我没记错的话,可能有两种基于密钥的解决方案: 喜欢

    public final class IntArrayKey {
        private final int[];
        ...
    }
    

    (遭受可能的阵列突变或防御阵列克隆),或 像

    public final class Key<T> {
        private final Predicate<T> equals;
        private final IntSupplier hashCode;
        public static Key<int[]> of(final int[] array) {
            return new Key<>(that -> Arrays.equals(array, that), () -> Arrays.hashCode(array));
        }
    

    保持通用性。

    我能想到的另一种解决方案可能是使用 fastutilTrove 而不是 List&lt;Integer&gt;(例如 IntList 覆盖equalshashCode 正确)。不确定是否值得 现在添加所有可能的解决方案(也许还有更多?)。 :)

    【讨论】:

    • 为了完整起见,值得添加自定义键类解决方案,因为创建这样的键类实例在内存使用方面可能更便宜(尤其是对于不一定使用整数的大型原始数组来自Integer 缓存 + 它适用于其他“奇异”情况,例如double[]char[]),而且速度更快(特别是对于面向散列的大型集合,其中使用线性搜索是性能杀手)。
    • 好点,@fluffy,谢谢。如果您愿意,您可以将其添加到您自己的答案中,或者我可以将其添加到我的答案中。现在我已经提到了空间惩罚,灵感来自你的评论。
    • 是的,如果需要,请添加:涵盖大多数/所有情况的单一答案更好(我认为)。此外,如果我没记错的话,可以有两种基于密钥的解决方案:public final class IntArrayKey{ private final int[]; ... } 之类的东西(遭受可能的数组突变或防御性数组克隆),或者public final class Key&lt;T&gt; { private final Predicate&lt;T&gt; equals; private final IntSupplier hashCode; public static Key&lt;int[]&gt; of(final int[] array) { return new Key&lt;&gt;(that -&gt; Arrays.equals(array, that), () -&gt; Arrays.hashCode(array)); } 之类的东西以保持其通用性。
    • 我能想到的另一种解决方案可能是使用fastutilTrove 而不是List&lt;Integer&gt;(例如IntList 可以正确覆盖equalshashCode)。现在不确定是否值得添加所有可能的解决方案(也许还有更多?)。 :)
    【解决方案2】:

    您可以使用TreeSet 代替HashSet 与比较两个数组的内容 而不是两个数组对象的哈希码。然后你可以使用TreeSet.contains方法如下:

    int[] a = {0, 0};
    int[] b = {0, 0};
    int[] c = {0, 0};
    
    HashSet<int[]> hashSet = new HashSet<>();
    TreeSet<int[]> treeSet = new TreeSet<>(Arrays::compare);
    
    hashSet.addAll(List.of(a, b, c));
    treeSet.addAll(List.of(a, b));
    
    System.out.println(hashSet.size()); // 3
    System.out.println(treeSet.size()); // 1
    
    System.out.println(treeSet.contains(a)); // true
    System.out.println(treeSet.contains(b)); // true
    System.out.println(treeSet.contains(c)); // true
    

    【讨论】:

      猜你喜欢
      • 2016-05-28
      • 2021-07-17
      • 1970-01-01
      • 2011-06-20
      • 1970-01-01
      • 1970-01-01
      • 2012-08-14
      相关资源
      最近更新 更多