Sortowanie tablicy obiektów

Odpowiedz Nowy wątek
2019-10-08 21:54
0

Mam zadanie gdzie do posortowania jest tablica obiektów klasy Event, która to ma 2 pola String. Jednym z nich jest Data i to po niej będziemy sortować. W zadaniu zaznaczono, że nie można korzystać z typów do przechowywania daty i czasu i z kolekcji. Tylko tablice.
Mój pomysł jest prosty w zamyśle: sortuje najpierw po roku, później po miesiącach i po dniach. Na razie napisałem tylko sortowanie po roku. Zanim przejdę dalej może podsuniecie pomysł jak zrobić to w bardziej elegancki sposób bo moje rozwiązanie nie podoba mi się w ogóle. A oto moje wypociny:


    public static void sortEvent(Event[] events) {

        Event[] sortedEventList = new Event[events.length];
        Event[] unSortedEventList = new Event[events.length];

        //searching first year
        int index = 0;
        if (!allYearsIsTheSame(events)) {
            for (int i = 0; i < events.length; i++) {

                sortedEventList[i] = events[findMinYer(events)];
                index = 0;
                for (int j = 0; j < events.length; j++) {
                    if (j != findMinYer(events)) {
                        unSortedEventList[index] = events[j];
                        index++;
                    }
                }
                events = Arrays.copyOf(unSortedEventList, unSortedEventList.length);
            }
            System.out.println("sorted List: " + Arrays.toString(sortedEventList));

            }
        }

    static int findMinYer(Event[] events) {

        int year = Integer.MAX_VALUE;
        final int yearIndex = 2;
        String[] date;
        int minYearIndex = 0;

        for (int i = 0; i < events.length; i++) {
            if(events[i] != null) {
                date = events[i].getEventDate().split("\\.");

                if (Integer.parseInt(date[yearIndex]) < year) {
                    year = Integer.parseInt(date[yearIndex]);
                    minYearIndex = i;
                }
            }
        }
        return minYearIndex;
    }

    public static boolean allYearsIsTheSame(Event[] events) {

        int year;
        final int yearIndex = 2;
        String[] date;

        for (int i = 0; i < events.length - 1; i++) {

            date = events[i].getEventDate().split("\\.");
            year = Integer.parseInt(date[yearIndex]);

            if (year != Integer.parseInt(events[i + 1].getEventDate().split("\\.")[yearIndex]))
                return false;
        }
        return true;
    }
A nie Możesz napisać swojej klasy Data i tam zamknąć trochę złożoności? - lion137 2019-10-08 22:09
Tak. Jednak to tylko zmieni lokalizacje kodu a nie sposób w jaki sortowanie się odbywa. - manifestor 2019-10-08 22:24

Pozostało 580 znaków

2019-10-09 06:50
0

java.util.Arrays.sort i własny comparator do eventow mieści się w dopuszczalnych rozwiązaniach?

Pozostało 580 znaków

2019-10-09 10:23
1
manifestor napisał(a):

Mój pomysł jest prosty w zamyśle: sortuje najpierw po roku, później po miesiącach i po dniach. Na razie napisałem tylko sortowanie po roku.

To jest zły pomysł. Sortowanie ma być jedno, z trójpolowym porównywaniem. Nie wolno ci użyć gotowych klas, ale MyComparator trzeba zrobić, nawet i do 'ręcznego' sortowania

Pozostało 580 znaków

2019-10-09 21:45
0

Wyszło tak:

Nadpisana metoda compareTo w klasie Event

    @Override
    public int compareTo(Event o2) {

        //Jeśli event o1 jest pierwszy to zwróć 1
        //Jeśli event jest tego samego dnia to zwróć 0
        //Jeśli event o2 jest pierwszy to zwróć 2

        String[] dateO1 = this.getEventDate().split("\\.");
        String[] dateO2 = o2.getEventDate().split("\\.");

        if (Integer.parseInt(dateO1[2]) == Integer.parseInt(dateO2[2])) {
            if (Integer.parseInt((dateO1[1])) == Integer.parseInt(dateO2[1])) {
                if (Integer.parseInt(dateO1[0]) == Integer.parseInt(dateO2[0])) {
                    return 0;
                } else if (Integer.parseInt(dateO1[0]) < Integer.parseInt(dateO2[0])) {
                    return 1;
                } else return 2;

            } else if (Integer.parseInt(dateO1[1]) < Integer.parseInt(dateO2[1])) {
                return 1;
            } else return 2;

        } else if (Integer.parseInt(dateO1[2]) < Integer.parseInt(dateO2[2])) {
            return 1;
        } else return 2;
    }

Klasa EventUtils


  public static void swapEvents(Event[] events, int i, int j) {
        Event tmpEvent = events[i];
        events[i] = events[j];
        events[j] = tmpEvent;
    }

    public static void sortEvent(Event[] events) {

        boolean change = false;

        do{
            change = false;
            for (int i = 0; i < events.length-1; i++) {
                for (int j = i + 1; j < events.length; j++) {
                    if (events[i].compareTo(events[j]) == 2) {
                        swapEvents(events, i, j);
                        change = true;
                        j = events.length;
                    }
                }
            }
        }while (change);

        System.out.println("Posortowane eventy: " + Arrays.toString(events));

    }

Pozostało 580 znaków

2019-10-09 22:04

1) Jest taki interfejs: https://docs.oracle.com/javas[...]api/java/lang/Comparable.html, który ma metodę compreTo, która powinna zwracać "a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.".
2) Jest coś takiego jak https://pl.wikipedia.org/wiki/Porządek_leksykograficzny - sprawdź jak zadziała Twoje compareTo, jak zredukujesz implementację do prymitywnego return this.getEvnetDate().compareTo(o2.getEventDate()); (prymitywnego, bo wywali się na Eventach, które nie mają ustawionej daty).
3) Dlaczego nie użyjesz po prostu API do sortowania tablic ?

1) No i przecież w tym kodzie powyżej jest właśnie ta metoda compareTo z interfejsu Comparable. 2) prymitywne compareTo porównuje pierwszy różniący się człon a ja potrzebuje porównać wszystkie. Nie wywali się jeśli tworzenie obiektu Event będzie wymagało podania daty. 3) Nie pmyślałem o tym :) - manifestor 2019-10-10 20:14
1) Nie jest. Kontrakt interfejsu mówi tyle, że compareTo powinna zwracać : ujemny int, 0, dodatni int w przypadku, gdy obiekt jest "mniejszy,równy,większy". Ty zwracasz 0,1,2, więc nie spełniasz kontraktu, to raz. Z kodu, który pokazałeś nie wynika, czy interfejs jest implementowany przez Event, czy po prostu tak sobie nazwałeś metodę i myślisz, że jest ok. 2) Jakie człony? Porównujesz stringa "alfabetycznie" i masz 2001.01.01 < 2001.01.05, AAA<AAB itd. Chyba, że format daty jest ułomny i dopuszczasz 2001.5.2, 2001.03.04 etc. - yarel 2019-10-10 20:21
1) Event implementuje comparable a w metodzie compareTo tak sobie po prostu wymyśliłem. Rozumiem, że nie jest to dobra praktyka. Jeśli chodzi o te człony... mój format daty był 01.01.2001 i wtedy gdy dni się różniły to roku już compareTo nie porównuje. Ale zmieniłem format zaczynający się od roku i jest ok. - manifestor 2019-10-10 21:00

Pozostało 580 znaków

2019-10-10 06:23
1
yarel napisał(a):

2) Jest coś takiego jak https://pl.wikipedia.org/wiki/Porządek_leksykograficzny - sprawdź jak zadziała Twoje compareTo, jak zredukujesz implementację do prymitywnego return this.getEvnetDate().compareTo(o2.getEventDate()); (prymitywnego, bo wywali się na Eventach, które nie mają ustawionej daty).

A co innego można zrobić niż się wywalić. NullPointerException to twój przyjaciel.

Jak zaimplementują w Javie 14 ten ficzur - to NPE będzie jeszcze fajniejszy:
https://bugs.openjdk.java.net/browse/JDK-8218628


Bardzo lubie Singletony, dlatego robię po kilka instancji każdego.
edytowany 1x, ostatnio: jarekr000000, 2019-10-10 06:25
Już widzę oczami wyobraźni te pociągi: lokomotywa.wagon1.wagon2.wagon3...wagonN i jeszcze większe łąmanie prawa Demeter :D - Kamil Żabiński 2019-10-10 11:37

Pozostało 580 znaków

2019-10-10 07:44
0
jarekr000000 napisał(a):
yarel napisał(a):

2) Jest coś takiego jak https://pl.wikipedia.org/wiki/Porządek_leksykograficzny - sprawdź jak zadziała Twoje compareTo, jak zredukujesz implementację do prymitywnego return this.getEvnetDate().compareTo(o2.getEventDate()); (prymitywnego, bo wywali się na Eventach, które nie mają ustawionej daty).

A co innego można zrobić niż się wywalić. NullPointerException to twój przyjaciel.

Można mieć logikę sortowania NULLS FIRST/LAST. Co lepsiejsze, to już do osobnego rozważania dla konkretnego przypadku.

Jak zaimplementują w Javie 14 ten ficzur - to NPE będzie jeszcze fajniejszy:
https://bugs.openjdk.java.net/browse/JDK-8218628

Wietrzę "potężny" pattern wynikający z ficzera :D

catch (NullPointerException e) {
  if (e.getMessage().matches(...) ) { 
  } 
  ... 
  else { ... }

Dobrze, że Oracle pomyślał (-XX:ShowCodeDetailsInExceptionMessages) o zgodności wstecznej.

Pozostało 580 znaków

2019-10-10 07:59
1

Walczę z nullistami od dłuższego czasu i sprawdzanie nulli też uważam za kiepski pomysł, o ile nie są one explicite dopuszczone.
Explicit null jest wtedy kiedy pole jest np. adnotowane jako @Nullable. Co i tak nadal jest słabe, bo najlepiej jak nulli nie ma w ogóle. A pola opcjonalne są po prostu opakowane w Option.
Nie da się wtedy tego przeoczyć.

Jakiś czas temu zaczęło mnie męczyć przedzieranie się przez tony null checków, żeby dojść do tej jednej / biednej linijki z logiką.
Z drugiej strony tutaj oczywiście odpornośc na null dość łatwo zrobić... tylko nie wiadomo czy warto. Szybie wywalenie się to też zaleta. Zamiast wstawiać te nulle z przodu, czy z tyłu i pchać problem do następnej warstwy....


Bardzo lubie Singletony, dlatego robię po kilka instancji każdego.
edytowany 1x, ostatnio: jarekr000000, 2019-10-10 08:00

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0