【问题标题】:Check if an arrayList contains an array检查 arrayList 是否包含数组
【发布时间】:2021-05-27 09:53:30
【问题描述】:

我有一个包含数组的 arrayList。如何检查 arrayList 是否包含指定的数组?我使用了.contains 方法,它返回false 而不是预期的true

import java.util.ArrayList;
import java.util.Arrays;

public class main {
    public static void main(String[] args) {
        ArrayList<String[]> action = new ArrayList<String[]>();
        action.add(new String[]{"appple", "ball"});
        String[] items = new String[]{"appple", "ball"};
        if (action.contains(new String[]{"appple", "ball"})) {
            System.out.println("Yes");
        }
        System.out.println(action.contains(items)); // False
    }
}

【问题讨论】:

  • 这看起来从一开始就是一个糟糕的设计。考虑创建一个List&lt;List&lt;String&gt;&gt; 或将数组包装在一个使用java.util.Arrays.equalsjava.util.Arrays.hashCode 作为自己的equals 和hashCode 方法的类中。

标签: java arrays arraylist contains


【解决方案1】:

根据 JavaDocs,“contains”方法是使用“equals”和“hashCode”方法来检查对象是否被包含。

一个主要问题: 你知道数组的“等于”的实现是什么吗?

检查一下,你可能会明白你的代码的执行结果(提示:==)。

正如“Hovercraft Full Of Eels”所说,更好的设计将使用一些集合的列表,您可以理解/控制它的“equals”和“hashCode”方法。

【讨论】:

    【解决方案2】:

    当您创建不同的数组时(即使内容相同),contains 将导致 false。

    但是,如果你这样做:

     List<String[]> action = new ArrayList<String[]>();
     String[] items = new String[]{"apple","ball"};   
     action.add(items);
     if (action.contains(items)) 
         System.out.println("Yes");
    

    这将打印Yes。 此外,一些行为示例:

     String[] items = new String[]{"apple","ball"};   
     action.add(items);
     String[] clone = items.clone();
     String[] mirror = items;
    
     action.contains(clone); // false 
     action.contains(mirror); // true
    
     items[0]="horse";
     System.out.println(mirror[0]);        // "horse"
     System.out.println(clone[0]);         // "apple"
     System.out.println(action.get(0)[0]); // "horse"
    
     mirror[1]="crazy";
     System.out.println(clone[1]);         // "ball"
     System.out.println(action.get(0)[1]); // "crazy"
     System.out.println(items[1]);         // "crazy"
    
     clone[1]="yolo";
     System.out.println(action.get(0)[1]); // "crazy"
     System.out.println(items[1]);         // "crazy"
     System.out.println(mirror[1]);        // "crazy"
    
     System.out.println(action.get(0).hashCode());    //2018699554
     System.out.println(items.hashCode());            //2018699554
     System.out.println(clone.hashCode());            //1311053135
     System.out.println(mirror.hashCode());           //2018699554
    

    自定义“contains

    这里的问题是,如果您想在之后搜索特定数组,则会丢失引用并且无法搜索项目,甚至无法复制具有相同精确值的数组。

    作为一种解决方法,您可以实现自己的contains 方法。比如:

    如果你想获取索引:

    static int indexOfArray(List<String[]> list, String[] twin)
    {        
       for (int i=0;i<list.size();i++)
          if (Arrays.equals(list.get(i),twin))
               return i;
       return -1;
    }
    

    然后,这样称呼它:

    String[] toSearch = new String[]{"apple","ball"};
    int index = indexOfArray(action, toSearch); 
    
    if (index>0) 
        System.out.println("Array found at index "+index);
    else
        System.out.println("Array not found");
    

    如果索引大于-1,您可以通过以下方式获取原始数组:

    String[] myArray = action.get(index);
    

    HashMap + 标识符

    另一种方法是将数组存储到HashMap 中,方法是为每个数组声明一个标识符。例如:

    Base64 ID

    这将为相同的值提供相同的结果,因为编码值基于条目,而不是对象的引用。

     static String getIdentifier(String[] array)
     {
        String all="";
        for (String s : array)
            all+=s;
        return Base64.getEncoder().encodeToString(all.getBytes());
     }
    

    然后你可以:

    Map<String, String[]> arrayMap= new HashMap<>();
    String[] items = new String[]{"apple","pear", "banana"}; // *[1234] 
    action.add(items);
    arrayMap.put(getIdentifier(items), items); // id = QUJDYWFh
    //....
    //Directly finding the twin will fail
    String[] toSearch = new String[]{"apple","pear", "banana"}; // *[1556]
    System.out.println(action.contains(toSearch)); // false
    
    //But if we get the identifier based on the values
    String arrId = getIdentifier(toSearch); // id = QUJDYWFh
    System.out.println(action.contains(arrayMap.get(arrId)));  //true
    
    //arrayMap.get(arrId)->  *[1234]
    //.....
    

    姓名

    选择一个代表名称并将其用作 Id

    Map<String, String[]> arrayMap= new HashMap<>();
    String[] items = new String[]{"apple","pear", "banana"};
    action.add(items);
    arrayMap.put("fruits", items);
    //...
    System.out.println(action.contains(arrayMap.get("fruits"))); // true  
        
    

    【讨论】:

    • The issue here is that if you want to search for an specific array afterwards, you'd lose the references and searching an item wouldn't be possible, not even replicating the array with the same exact values. 超级奇怪。我在哪里可以了解更多信息?
    • 这正是您的问题中发生的情况。您知道哪些值是,您逐点复制数组,但仍然告诉您它不包含它。这是因为您的第一个数组的哈希码是 1234,而第二个数组的哈希码是 1554(简化)。即使具有相同的值,它们也是不同的对象。
    • String[] copy = items.clone() 如果您调用 items.equals(copy),则结果为 false。为什么?测试一下。 System.out.println(copy.hashCode());System.out.println(items.hashCode()); 哈希码不同,即使是克隆。
    【解决方案3】:

    “包含”方法比较等效的 hashCode 值。

    所以如果你像下面这样*,它就会通过。

    public class main {
        public static void main(String[] args) {
            ArrayList<String[]> action = new ArrayList<String[]>();
    
            String[] items = new String[]{"appple","ball"};
            action.add(items);
    
            System.out.println("TO STRING");
            System.out.println("--"+action.get(0));
            System.out.println("--"+new String[]{"apple","ball"});
    
            System.out.println("HASHCODES");
            String[] sameValues = new String[]{"apple","ball"};
            System.out.println("--"+action.get(0).hashCode());    
            System.out.println("--"+items.hashCode());           
            System.out.println("--"+sameValues.hashCode());    
           
            System.out.println("CONTAINS");
            System.out.println("--"+action.contains(items));  // *this
            System.out.println("--"+action.contains(sameValues));
            System.out.println("--"+action.contains(new String[]{"apple","ball"}));
     
        }
    }
    

    结果是:

    TO STRING
    --[Ljava.lang.String;@7b1d7fff
    --[Ljava.lang.String;@299a06ac
    HASHCODES
    --1243554231
    --1243554231
    --2548778887
    CONTAINS
    --true
    --false
    --false
    

    关于打印数组时显示的代码,这些不会覆盖toString(),所以你得到:

    getClass().getName() + '@' + Integer.toHexString(hashCode())

    例如:

    [Ljava.lang.String;@7b1d7fff

    • [代表一维数组
    • Ljava.lang.String 代表类型
    • @
    • 7b1d7fff 哈希码的十六进制表示

    但是,如果要比较值,有以下方法。

    public class main {
        public static void main(String[] args) {
    
            String[] items = new String[]{"apple","ball"};
    
            ArrayList<String> action = new ArrayList<>(Arrays.asList(items));
    
            if (action.contains("apple")) {
                System.out.println("Yes");
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      您可以遍历此列表,并为每个元素(即数组)调用Arrays.equals 方法来检查数组的相等性,直到第一次匹配,或者如果没有匹配到列表的末尾。在这种情况下,它可以为每个元素返回true

      List<String[]> list = List.of(
              new String[]{"appple", "ball"},
              new String[]{"appple", "ball"});
      
      String[] act = new String[]{"appple", "ball"};
      
      System.out.println(list.stream()
              .anyMatch(arr -> Arrays.equals(arr, act))); // true
      

      该方法内部为数组的每个元素调用String#equals方法,即String,所以这段代码也返回true

      List<String[]> list = List.of(
              new String[]{new String("appple"), new String("ball")},
              new String[]{new String("appple"), new String("ball")});
      
      String[] act = new String[]{new String("appple"), new String("ball")};
      
      System.out.println(list.stream()
              .anyMatch(arr -> Arrays.equals(arr, act))); // true
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 2014-12-03
        • 1970-01-01
        • 2019-08-10
        • 2013-04-22
        相关资源
        最近更新 更多