Как сравнить равенство списков массивов с современной Java?
У меня есть два списка массивов.
Как я могу легко сравнить равенство этих С Java 8 и его характеристики, без использования внешних библиотек? Я ищу "лучшее" (более высокий уровень, более короткий, более эффективный) решение, чем такой код грубой силы (непроверенный код, может содержать опечатки и т. д., а не вопрос):
boolean compare(List<String[]> list1, List<String[]> list2)
{
// tests for nulls etc omitted
if(list1.size() != list2.size()) {
return false;
}
for(i=0; i<list1.size(); ++i) {
if(!Arrays.equals(list1.get(i), list2.get(i))) {
return false;
}
}
return true;
}
или, если нет лучшего способа, это тоже правильный ответ.
бонус: если Java 9 предлагает еще лучший способ, что whaterver Java 8 может предложить, не стесняйтесь упомянуть об этом.
Edit: посмотрев на комментарии, и увидев, как этот вопрос стал умеренно горячим, я думаю "лучше " должен включать в себя первую проверку длины всех массивов, прежде чем проверять содержимое массива, потому что это может найти неравенство гораздо быстрее, если внутренние массивы длинные.
5 ответов:
1) решение на основе Java 8 потоков:
List<List<String>> first = list1.stream().map(Arrays::asList).collect(toList()); List<List<String>> second = list2.stream().map(Arrays::asList).collect(toList()); return first.equals(second);2) гораздо более простое решение (работает в Java 5+):
return Arrays.deepEquals(list1.toArray(), list2.toArray());3) что касается вашего нового требования (сначала проверить длину содержащихся строковых массивов), вы можете написать общий вспомогательный метод, который проверяет равенство для преобразованных списков:
<T, U> boolean equal(List<T> list1, List<T> list2, Function<T, U> mapper) { List<U> first = list1.stream().map(mapper).collect(toList()); List<U> second = list2.stream().map(mapper).collect(toList()); return first.equals(second); }тогда решение может быть:
return equal(list1, list2, s -> s.length) && equal(list1, list2, Arrays::asList);
The
forцикл по крайней мере может быть потоковым, что приводит к:return (list1.size()==list2.size() && IntStream.range(0, list1.size()) .allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
используя
zip(который происходит от лямбда b93) функция от https://stackoverflow.com/a/23529010/755183, код может выглядеть так:boolean match = a.size() == b.size() && zip(a.stream(), b.stream(), Arrays::deepEquals). allMatch(equal -> equal)обновление
чтобы сначала проверить размер массивов, а затем содержимое, это может быть решением для рассмотрения
final boolean match = a.size() == b.size() && zip(a.stream(), b.stream(), (as, bs) -> as.length == bs.length). allMatch(equal -> equal) && zip(a.stream(), b.stream(), Arrays::deepEquals). allMatch(equal -> equal);
вы может используйте поток, если списки являются списками произвольного доступа (так что вызов
getбыстро - обычно постоянное время), что приводит к://checks for null and size before boolean same = IntStream.range(0, list1.size()).allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));однако вы можете указать в качестве параметров некоторые реализации, которые не являются (например, LinkedLists). В этом случае лучше всего использовать итератор явно. Что-то вроде:
boolean compare(List<String[]> list1, List<String[]> list2) { //checks for null and size Iterator<String[]> iteList1 = list1.iterator(); Iterator<String[]> iteList2 = list2.iterator(); while(iteList1.hasNext()) { if(!Arrays.equals(iteList1.next(), iteList2.next())) { return false; } } return true; }
вы можете передавать по одному списку и сравнивать с каждым элементом другого с помощью итератора:
Iterator<String[]> it = list1.iterator(); boolean match = list1.size() == list2.size() && list2.stream().allMatch(a -> Arrays.equals(a, it.next()));использование итератора вместо
get(index)метод в первом списке лучше, потому что не имеет значения, является ли списокRandomAccessили нет.Примечание: это работает только с последовательный поток. Использование параллельного потока приведет к неверным результатам.
EDIT: согласно вопросу последнего редактирования, который указывает, что это будет лучше проверить длину каждой пары массивов заранее, я думаю, что это может быть достигнуто с небольшой модификацией моего предыдущего кода:
Iterator<String[]> itLength = list1.iterator(); Iterator<String[]> itContents = list1.iterator(); boolean match = list1.size() == list2.size() && list2.stream() .allMatch(a -> { String[] s = itLength.next(); return s == null ? a == null : a == null ? s == null : a.length == s.length; }) && list2.stream() .allMatch(a -> Arrays.equals(a, itContents.next()));здесь я использую два итератора и потокового
list2дважды, но я не вижу другого способа проверить все длины перед проверкой содержимого первой пары массивов. Проверка длины является null-safe, в то время как проверка содержимого делегируетсяArrays.equals(array1, array2)метод.
Comments