Java: проверка равенства массивов (порядок не имеет значения)
У меня есть два массива String
, скажем:
String[] s1 = {"a","b","c"}
String[] s2 = {"c","a","b"}
//эти массивы должны быть равны
Я хотел проверить их равенство "самым чистым" способом.
Я попытался использовать Arrays.equals(s1,s2)
, но я получаю ложный ответ. Я предполагаю, что этот метод касается порядка элементов, и я не хочу, чтобы это имело значение.
Не могли бы вы рассказать мне, как я могу сделать это красиво?
Ответы
Ответ 1
- Arrays.sort(s1);
- Arrays.sort(с2);
- Arrays.equals(s1, s2);
Если вы не хотите изменять исходные массивы
Arrays.equals( Arrays.sort( Arrays.copyof(s1,s1.length)),
Arrays.sort( Arrays.copyof(s2,s2.length)) );
Arrays.sort() использует оптимизированную быструю сортировку, которая является nlog (n) для среднего, но O (n2) в худшем случае. Из java-документов. Поэтому в худшем случае O (n2), но в большинстве случаев это будет O (nlogn).
Алгоритм сортировки - это настроенная быстродействующая сортировка, адаптированная из Jon L. Bentley и M. Douglas McIlroy "Engineering a Sort Function", Software-Practice and Experience, Vol. 23 (11), с. 1249-1265 (ноябрь 1993 года). Этот алгоритм обеспечивает производительность n * log (n) на многих наборах данных, которые приводят к снижению производительности в быстродействующих секторах до квадратичной производительности.
Ответ 2
Другие предложили сортировать массивы. Но так как вы ищете "самое чистое" решение, я думаю, что оригинальные массивы не следует трогать. Следовательно:
List<String> l1 = new ArrayList<String>(Arrays.asList(s1));
List<String> l2 = new ArrayList<String>(Arrays.asList(s2));
Collections.sort(l1);
Collections.sort(l2);
boolean outcome = l1.equals(l2);
Ответ 3
String[] s1 = {"a","b","c"};
String[] s2 = {"b","c","a"} ;
Arrays.sort(s1);
Arrays.sort(s2);
if(Arrays.equals(s1, s2)){
System.out.println("ok");
}
Ответ 4
Если вы используете Коллекции Eclipse (ранее Коллекции GS), вы можете использовать Bag
, чтобы выяснить, равны ли эти два массива.
String[] s1 = {"a", "b", "c", "c"};
String[] s2 = {"c", "a", "b", "c"};
Bag<String> h1 = HashBag.newBagWith(s1);
Bag<String> h2 = HashBag.newBagWith(s2);
Assert.assertEquals(h1, h2);
Сумки (также известные как мультимножества) считаются равными, если они имеют одинаковое количество вхождений каждого элемента. Заказ не имеет значения, и он правильно обрабатывает повторяющиеся элементы. Преимущество использования мешка, поддерживаемого хеш-таблицей, заключается в том, что создание занимает линейное время. Сортировка и принимает O (n log n).
Примечание: я являюсь коммиттером для коллекций Eclipse
Ответ 5
Человеческий путь:
Итерации по первому массиву, проверка наличия каждого элемента во втором массиве, а затем выполнение этого же для второго массива в первом массиве. Время: n ^ 2. Обратите внимание, что этот метод предполагает, что ни один элемент не повторяется. Если бы это было так, вам нужно было бы для каждого элемента, который вы проверяете, вернуться к началу и подсчитать, сколько экземпляров этого элемента есть (скажем, X), и только считать успехом, как найти X-й элемент в второй массив. Это позволит устранить необходимость второй проверки и оставить упражнение для читателя (если вы так склонны, то есть.)
boolean equal(String[] arr1, String[] arr2) {
if(arr1.length != arr2.length) return false; // obviously
main_loop:
for(int i = 0; i < arr1.length; i++) {
for(int j = 0; j < arr2.length; j++) {
if(arr1[i].equals(arr2[j]))
break main_loop;
}
return false;
}
main_loop:
for(int i = 0; i < arr2.length; i++) {
for(int j = 0; j < arr1.length; j++) {
if(arr2[i].equals(arr1[j]))
break main_loop;
}
return false;
}
// having got through both loops, we can now return true
}
Более продвинутый способ: сортировать оба массива и перемещаться по ним. Время: n lg n
boolean equals(String[] arr1, String[] arr2) {
if(arr1.length != arr2.length) return false;
String[] copy1 = Arrays.copyOf(arr1,arr1.length); // java.util.Arrays
String[] copy2 = Arrays.copyOf(arr2,arr2.length); // java.util.Arrays
Arrays.sort(copy1);
Arrays.sort(copy2);
for(int i = 0; i < copy1.length; i++) {
if(!copy1[i].equals(copy2[i])
return false;
}
return true;
}
Еще более продвинутый способ: использовать hashmap, добавляя для счетчиков первого массива строк, удаляя для счетчиков второго массива строк. Когда вы все равно, все значения должны быть равны нулю.
boolean equal(String[] arr1, String[] arr2) {
if(arr1.length != arr2.length) return false;
Map<String, Integer> map1 = new HashMap<String,Integer>();
for(String str : arr1) {
if(!map.containsKey(str)) {
map.put(str, 1);
} else {
map.put(str, map.get(str) + 1); // add to count inthe map
}
}
for(String str : arr1) {
if(!map.containsKey(str)) {
return false; // we have an element in arr2 not in arr1 - leave now
} else {
map.put(str, map.get(str) - 1); // remove to count inthe map
}
}
for(Integer count : map.values()) {
if(count.intValue() != 0) return false;
}
return true;
}
Ответ 6
Set::equals
Для этого вам не нужны внешние библиотеки. Set<>
уже имеет метод equals
, который делает независимое от заказа сравнение.
public static <T> boolean areArraysEquivalent(T[] ary1, T[] ary2) {
if (ary1 == null) {
return ary2 == null;
}
if (ary2 == null) {
return false;
}
List<T> list1 = Arrays.asList(ary1);
List<T> list2 = Arrays.asList(ary2);
return areListsEquivalent(list1, list2);
}
public static <T> boolean areListsEquivalent(List<T> list1, List<T> list2) {
if (list1 == null) {
return list2 == null;
}
if (list2 == null) {
return false;
}
Set<T> set1 = new HashSet<>(list1);
Set<T> set2 = new HashSet<>(list2);
return set1.equals(set2);
}
Ответ 7
Я предполагаю, что это для школы.
Возможные стратегии:
- используйте Arrays.sort для сортировки обоих массивов, а затем используйте цикл для сравнения s1 [i] с s2 [i]
- используйте цикл и для каждого элемента s1 посмотрите на элементы s2, чтобы найти, содержит ли он тот же
- поместите элементы s1 в hashset, а затем используйте цикл на s2 и посмотрите, находятся ли ваши элементы в s1
Ответ 8
Сначала я бы отсортировал 2 массива, а затем сравнил строки за строкой...
public boolean areArraysEqual (String[] array1,String[] array2){
if (s1.length != s2.length){
return false;
}
java.util.Arrays.sort(s1);
java.util.Arrays.sort(s2);
for (int i=0;i<s1.length;i++){
if (! s1[i].equals(s2[i])){
return false;
}
}
return true;
}
Ответ 9
Если часто приходится сравнивать массивы друг с другом, не изменяя их содержимое, может оказаться полезным определить тип, который инкапсулирует неизменяемый массив, его отсортированную версию, число long
, которое гарантировано уникальным и по крайней мере, в основном коррелирует с возрастом объекта и ссылкой на исходный нуль на другой более старый объект, который, как известно, соответствует. Также может быть полезно кэшировать хеш-значение, которое объединяет хеш-значения всех элементов массива.
Используя такой подход, сортировка будет требоваться при первом сравнении объектов с чем-либо (что-либо), но не после этого. Кроме того, если объекты X и Y оба найдены равными Z, то сравнение между X и Y может сообщать о них как о равном без фактического изучения содержимого массива (если Z старше X и Y, то оба будут сообщать себя как равные к одному и тому же более старому объекту, если X является самым младшим, а Y - самым старым, X будет знать его равным Z, а Z будет знать его равным Y. Когда X следующий по сравнению с чем-то, он узнает, что самая старая вещь, которую он знал быть равным Y, поэтому он, конечно, будет равен Y.
Такой подход обеспечит преимущества производительности сравнений по сравнению с интернированием, но без необходимости использования интернированного словаря.
Ответ 10
Для небольших массивов я использовал бы Arrays.sort
и Arrays.equals
, как предложили другие. Для больших массивов вы можете использовать следующее решение, которое имеет лучшую временную сложность - O(n)
, а не O(n log n)
.
public static boolean haveSameElements(Object[] arr1, Object[] arr2) {
return arr1.length == arr2.length && counts(arr1).equals(counts(arr2));
}
// Map.merge and method references require Java 8
private static <T> Map<T, Integer> counts(T[] arr) {
Map<T, Integer> map = new HashMap<>();
for (T t : arr)
map.merge(t, 1, Integer::sum);
return map;
}