检查一个ArrayList是否包含一个数组。

4
我有一个包含数组的 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
    }
}

1
这看起来从一开始就是一个糟糕的设计。考虑创建一个 List<List<String>> 或将数组包装在一个类中,该类使用 java.util.Arrays.equalsjava.util.Arrays.hashCode 作为其自己的 equals 和 hashCode 方法。 - Hovercraft Full Of Eels
4个回答

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]
//.....

名称.

选择一个代表性的名称并将其用作标识符。

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  
    

这里的问题是,如果您想在之后搜索特定的数组,您将失去引用,并且甚至无法搜索项目,也无法复制具有完全相同值的数组。非常奇怪。我在哪里可以阅读更多相关信息? - JJ123
1
这正是你问题中所发生的事情。你知道这些值是什么,你逐点复制该数组,但仍然告诉你它不包含它。这是因为你第一个数组的哈希码是1234,而第二个数组的哈希码是1554(简化)。即使具有相同的值,它们也是不同的对象。 - aran
1
如果你调用 String[] copy = items.clone(),然后再调用 items.equals(copy),结果会是 false。为什么呢?可以测试一下。System.out.println(copy.hashCode());System.out.println(items.hashCode()); 的 hashcode 不同,即使是克隆的对象也是如此。 - aran
1
@JJ123 https://dzone.com/articles/object-identity-and-equality-in-java@JJ123 https://dzone.com/articles/object-identity-and-equality-in-java - aran

2

'contains' 方法比较等价的 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");
        }
    }
}

2
你可以遍历这个列表,对于每个元素(即数组),调用 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 类型,分别调用内部的String#equals 方法进行比较,因此这段代码也会返回 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

0
根据JavaDocs,“contains”方法使用“equals”和“hashCode”方法来检查对象是否包含。
一个引导性问题: 你知道数组的“equals”实现是什么吗?
检查一下,你可能会理解代码的执行结果(提示:==)。
正如“Hovercraft Full Of Eels”所说,更好的设计将使用某个Collection的列表,你可以理解/控制它的“equals”和“hashCode”方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接