Как сравнить две коллекции Java списков строк с наименьшим количеством кода? - PullRequest
1 голос
/ 13 октября 2019

У меня есть два списка списков строк. Скажем,

  • Список 1: Элемент 1: Том, Харди, девятнадцать, студент;Элемент 2: Джон Траволта, двадцать, безработный;

  • Список 2: Элемент 1: Джон, Траволта, двадцать, безработный;Элемент 2: Том, Харди, девятнадцать, студент;

Я хочу сравнить эти списки и получить результат, что списки идентичны. Я мог бы создать класс «Персона» с полями имя, фамилия, возраст, род занятий. Затем создайте объекты. Но эти списки поступают из разных источников, поэтому объекты-элементы не будут одинаковыми. Я имею в виду, что Элемент1 из Списка1 (Том Харди) не будет тем же объектом, что и Элемент2 из Списка2 (Том Харди). Я должен был бы пойти в значения каждого элемента (объекта) и сравнить их, чтобы узнать, идентичны ли списки. По сути, я знаю, как это решить, мне просто интересно, как это можно сделать с помощью наименьшего количества кода.

Ответы [ 2 ]

0 голосов
/ 13 октября 2019

Наименьшее кодовое решение, которое я придумал:

public static void main(String[] args) {

    List<List<String>> list1 = Arrays.asList(
            Arrays.asList("Tom", "Hardy", "nineteen", "student"),
            Arrays.asList("John", "Travolta", "twenty", "unemployed"));

    List<List<String>> list2 = Arrays.asList(
            Arrays.asList("Tom", "Hardy", "nineteen", "student"),
            Arrays.asList("John", "Travolta", "twenty", "unemployed"));

    List<String> thesePeople = separatePropertiesToBigSinglePropertySortedList(list1);
    List<String> thosePeople = separatePropertiesToBigSinglePropertySortedList(list2);
    System.out.println("sortedListsAreEqual = " + thesePeople.equals(thosePeople));
}

private static List<String> separatePropertiesToBigSinglePropertySortedList(List<List<String>> listWithSeparateProperties) {
    List<String> resultList = new ArrayList<String>();
    for (List<String> propertiesOfPerson : listWithSeparateProperties) {
        String bigSingleProperty = "";
        for (String property : propertiesOfPerson) {
            bigSingleProperty = bigSingleProperty + property;
        }
        resultList.add(bigSingleProperty);
    }
    Collections.sort(resultList);
    return resultList;
}
0 голосов
/ 13 октября 2019

Да, вы бы задали класс Person для хранения частей данных.

В этом классе вы объявляете его для реализации интерфейса Comparable с compareTo метод выполнения любой работы, которую вы считаете равным.

Затем вы должны собрать эти Person объекты в различные списки .

И, наконец, сравните списки, вызвав их метод List::equals, который в свою очередь вызывает equals для каждого элемента, соответствующего каждому списку.

Я хочусравнить эти списки и получить результат, что списки идентичны.

Это работа List::equals.

Я мог бы создать класс "Person" с полями имя, фамилия, возраст, род занятий,Затем создайте объекты.

Да. И реализовать методы compareTo & equals & hashCode. Совет: См. Класс Objects.

Но эти списки происходят из разных источников, поэтому объекты-элементы не будут одинаковыми. Я имею в виду, что Элемент1 из Списка1 (Том Харди) не будет тем же объектом, что и Элемент2 из Списка2 (Том Харди). Я должен был бы пойти в значения каждого элемента (объекта) и сравнить их, чтобы узнать, идентичны ли списки.

Не совсем уверен, что вы имеете в виду здесь. Неужели списки могут содержать объекты, представляющие тех же лиц, но в другом порядке? Это проблема для List::equals, который рассматривает один и тот же порядок как условие равенства списков.

Два решения:

  • Напишите свою собственную утилиту проверки равенства для поиска второгосписок для каждого элемента в первом, после первой проверки, что оба списка имеют одинаковый размер.
  • Сортировка. Позвоните Collections.sort в оба списка, прежде чем звонить List::equals. Если вы хотите сохранить их текущий порядок, скопируйте каждый список в новые List объекты;это быстро, поскольку объекты элементов не копируются, копируются только ссылки на эти объекты элементов.

Я рекомендую второй вариант, а не первый.

В принципе, я знаю, как решить эту проблему, мне просто интересно, как это можно сделать с наименьшим количеством кода.

Просто сделайте это. Напишите класс Person и методы compareTo & equals & hashCode.

Нет вопроса "наименьшего кода", здесь только сравнение полей. Использование новых функциональных возможностей Java с лямбда-синтаксисом может сократить код. Но короткий код должен не быть вашей целью, если вы изучаете Java. Написание кода без лямбд, скорее всего, займет больше времени, но может помочь вам лучше понять, что происходит.

Выполните поиск в Интернете, чтобы найти множество примеров compareTo.

Что-то вроде следующего Person класса. Обратите внимание, как в моей интерпретации я опускаю occupation из соображений равенства и сравнения. Бизнес-правила, определяемые менеджером, а не прихотями программиста, определяют значение equals и compareTo. Кстати, помните, что hashCode должен соответствовать логике equals.

package work.basil.example;

import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.Comparator;
import java.util.Objects;

public class Person implements Comparable < Person >
{
    // Statics
    private static final Comparator < Person > NATURAL_ORDER_COMPARATOR =
            Comparator
                    .comparing( Person :: getDateOfBirth )
                    .thenComparing( Person :: getGivenName )
                    .thenComparing( Person :: getSurname );

    // Members
    private String givenName, surname;
    private LocalDate dateOfBirth;
    private String occupation;


    // Constructor
    public Person ( String givenName , String surname , LocalDate dateOfBirth , String occupation )
    {
        Objects.requireNonNull( givenName );
        Objects.requireNonNull( surname );
        Objects.requireNonNull( dateOfBirth );
        Objects.requireNonNull( occupation );
        this.givenName = givenName;
        this.surname = surname;
        this.dateOfBirth = dateOfBirth;
        this.occupation = occupation;
    }

    // `Comparable` interface
    public int compareTo ( Person other )
    {
        return NATURAL_ORDER_COMPARATOR.compare( this , other );
    }

    // `Object` overrides

    @Override
    public boolean equals ( Object o )
    {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        Person person = ( Person ) o;
        return getGivenName().equals( person.getGivenName() ) &&
                getSurname().equals( person.getSurname() ) &&
                getDateOfBirth().equals( person.getDateOfBirth() );
    }

    @Override
    public int hashCode ( )
    {
        return Objects.hash( getGivenName() , getSurname() , getDateOfBirth() );
    }

    @Override
    public String toString ( )
    {
        return "Person{" +
                "givenName='" + this.getGivenName() + '\'' +
                " | surname='" + this.getSurname() + '\'' +
                " | dateOfBirth=" + this.getDateOfBirth() +
                " | occupation='" + this.getOccupation() + '\'' +
                '}';
    }


    // Accessors

    public String getGivenName ( )
    {
        return givenName;
    }

    public String getSurname ( )
    {
        return surname;
    }

    public LocalDate getDateOfBirth ( )
    {
        return dateOfBirth;
    }

    public String getOccupation ( )
    {
        return occupation;
    }

    public int age ( ZoneId zoneId )
    {
        Objects.requireNonNull( zoneId );
        LocalDate today = LocalDate.now( zoneId );
        Period period = Period.between( this.getDateOfBirth() , today );
        return period.getYears() ;
    }
}

и некоторому примеру использования.

У нас есть два списка с одинаковыми объектами, но разным порядком. Мы сравниваем их как есть и после сортировки. Для удобства программиста мы используем потоки для сортировки. Обратите внимание, что потоки могут быть медленнее, чем обычные подходы, но вряд ли могут быть проблемой.

List < Person > thesePeople = List.of(
        new Person( "Tom" , "Hardy" , LocalDate.of( 1984 , Month.JANUARY , 23 ) , "Student" ) ,
        new Person( "John" , "Travolta" , LocalDate.of( 1965 , Month.MARCH , 17 ) , "Unemployed" )
);

List < Person > thosePeople = List.of(
        new Person( "John" , "Travolta" , LocalDate.of( 1965 , Month.MARCH , 17 ) , "Unemployed" ),
        new Person( "Tom" , "Hardy" , LocalDate.of( 1984 , Month.JANUARY , 23 ) , "Student" )
);

boolean givenListsAreEqual = thesePeople.equals( thosePeople );
boolean sortedListsAreEqual = thesePeople.stream().sorted().collect( Collectors.toList() ).equals( thosePeople.stream().sorted().collect( Collectors.toList() ) );

System.out.println( "givenListsAreEqual = " + givenListsAreEqual );
System.out.println( "sortedListsAreEqual = " + sortedListsAreEqual );

GivenListsAreEqual = false

sortedListsAreEqual = true

Кстати, вы увидите, что я храню дату рождения. Хранение возраста не имеет смысла, так как возраст может меняться изо дня в день. У меня есть age метод для возврата вычисленного значения.

...