Sortowanie po wybranym kryterium

0

Cześć, mam taki mały problem jak napisać sortowanie po wybranym kryterium w javie. Mam klase osoba która ma pola imie, nazwisko, wiek. Obiekty są przechowywane w liście. Chciał bym je posortować po wybranym kryterium więc zaimplementowałem interfejs Comparable, a to jest kod z metody która ma sortować:

    @Override
    public int compareTo(Osoba o) {
  
        System.out.println("Po czym chesz sortowac " + "\n" + "1-imie" + "\n" + "2-nazwisko");
        Scanner in = new Scanner(System.in);
        int odp;
        odp = in.nextInt();
        if(odp==1)
        {
          int porownanieImienia = imie.compareTo(o.imie);
                return porownanieImienia;  
        }
        else if(odp==2)
        {
            int porownanieNazwiska = nazwisko.compareTo(o.nazwisko);
                return porownanieNazwiska;    
        }
        else 
            return -1;
    } 

Niestety w konsoli kilka razy się wypisuje linijka

Po czym chesz sortowac " + "\n" + "1-imie" + "\n" + "2-nazwisko
(pewnie metoda sie wywołuje sama w sobie) fakt faktem sortuje ale niezbyt elegancko to wygląda. Macie jakiś pomysł jak to napisać tak aby jeden raz tylko podawać kryterium sortowania, czy może trzeba samemu pisać sortowanie :P ?

1

W metodzie compareTo() powinieneś zaimplemenować tylko samo porównanie - czyli test i zwrócenie wyniku - nic więcej.
Podczas sortowania takie porównanie między różnymi obiektami zapewne będzie się wykonywało wiele razy. Dlatego pozostałe kwestie, np. zapytanie użykownika wg. jakiego kryterium chce sortować dane, powinieneś realizować w innym miejscu. Możesz sobie zrobić jakieś pole statyczne w klasie Osoba, w którym zapamiętasz kryterium i odwołasz się do niego podczas porównywania obiektów.

0

Dodam jeszcze, że jeśli pytasz w metodzie compareTo(), to użytkownik może udzielać różnych odpowiedzi.
Drugi sposób (poza polem statycznym), to użycie tej https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#sort%28java.util.List,%20java.util.Comparator%29 wersji metody sort. Domyślne sortowanie Stringów jest leksykograficzne, a nie alfabetyczne. Wyniki sortowania metodą tą https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#sort%28java.util.List%29 wersją metody sort mogą Cię zaskoczyć: np. "adamski" po "Zagłobie", czy też "Łukasz" po "Zygmuncie**.

0

Fajnie można to zrobić wykorzystując wzorzec Composite, robiłem kiedyś na studiach porównywanie osób za pomocą komparatora z możliwością sortowania po kilku atrybutach jednocześnie. Kod jest nieco "studencki" ale może Ci się przyda:

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Person {

	private String LastName;
	private String FirstName;
	private String PESEL;
	private String City;
	private String Street;
	private int nr;
	
	public Person(String LastName, String FirstName, String PESEL, String City, String Street, int nr) {
		this.LastName = LastName;
		this.FirstName = FirstName;
		this.PESEL = PESEL;
		this.City = City;
		this.Street = Street;
		this.nr = nr;
	}
	
	public String toString() {
		return (LastName + "\t" + FirstName + "\t" + PESEL + "\t"+ City + "\t"+Street +"\t"+ nr + "\n");
	}
	
	public static class PersonComp implements Comparator<Person> {

		private List<Comparator<Person>> _child = new ArrayList<Comparator<Person>> ();
		private int wynik = 0;
		
		public void add( int i, Comparator<Person> arg1 ) {
			_child.add(i, arg1);
		}
		
		@Override
		public int compare(Person o1, Person o2) {
			for (int i=0; i<_child.size(); i++) {
				wynik = _child.get(i).compare(o1, o2);
				if (wynik != 0) return wynik;
			}
			return 0;
		}
		
	}
	
	public static class LastName implements Comparator<Person> {

		@Override
		public int compare(Person o1, Person o2) {
			return o1.LastName.compareToIgnoreCase(o2.LastName);
		}
		
	}
	
	public static class FirstName implements Comparator<Person> {

		@Override
		public int compare(Person o1, Person o2) {
			return o1.FirstName.compareToIgnoreCase(o2.FirstName);
		}
		
	}
	
	public static class City implements Comparator<Person> {

		@Override
		public int compare(Person o1, Person o2) {
			return o1.City.compareToIgnoreCase(o2.City);
		}
		
	}
	
	public static class Street implements Comparator<Person> {

		@Override
		public int compare(Person o1, Person o2) {
			return o1.Street.compareToIgnoreCase(o2.Street);
		}
		
	}
	
	public static class PESEL implements Comparator<Person> {

		@Override
		public int compare(Person o1, Person o2) {
			return o1.PESEL.compareToIgnoreCase(o2.PESEL);
		}
		
	} 
	
	public static class nr implements Comparator<Person> {

		@Override
		public int compare(Person o1, Person o2) {
			if (o1.nr > o2.nr) return 1;
			if (o1.nr == o2.nr) return 0;
			if (o1.nr < o2.nr) return -1;
			return 0;
		}
		
	}
	
}

a tutaj przykład wykorzystania:

import java.util.ArrayList;
import java.util.Collections;

public class Test { 

	public static void main(String[] args) {
		
		ArrayList<Person> arr = new ArrayList<Person>();
		
		arr.add(new Person("Kowalski", "Jan", "8511030788", "Kraków", "Karmelicka", 15));
		arr.add(new Person("Kowalski", "Grzegorz", "8510030788", "Kraków", "Karmelicka", 15));
		arr.add(new Person("Nowak", "Jan", "8511030789", "Kraków", "Karmelicka", 16));
		arr.add(new Person("Dudek", "Marian", "8609030008", "Poznań", "św. Marcina", 10));
		arr.add(new Person("Nowak", "Jan", "7509030008", "Poznań", "św. Marcina", 15));
		arr.add(new Person("Wiśniewska", "Justyna", "7001430008", "Poznań", "św. Marcina", 10));
		arr.add(new Person("Dudek", "Marian", "8609031111", "Warszawa", "Marszałkowska", 11));
		arr.add(new Person("Dudek", "Marian", "8609040008", "Poznań", "św. Marcina", 10));
		
		Person.PersonComp Comparator = new Person.PersonComp();
		Comparator.add(0, new Person.LastName());
		Comparator.add(1, new Person.FirstName());
		Comparator.add(2, new Person.PESEL());
		Comparator.add(3, new Person.City());
		Comparator.add(4, new Person.Street());
		Comparator.add(5, new Person.nr());
		
		
		System.out.println("Dane nieposortowane:");
		
		for (Person e :arr) 
			System.out.println(e);
		
		Collections.sort(arr, Comparator);
		System.out.println("\nDane posortowane:");
		for (Person e :arr) 
			System.out.println(e);
	}

}

0

@Rafss1014 do sortowania stringów możesz sobie pomóc String.CASE_INSENSITIVE_ORDER.
jak dla mnie zamiast implementować Comparable klasie Osoba robisz w klasie osoba poszczególne statyczne finalne komparatory:

class Account {
    private String name, surname;

    public Account(String name, String surname) {
        this.name = name;
        this.surname = surname;
    }

    public String getName() {
        return name;
    }

    public String getSurname() {
        return surname;
    }

    @Override
    public String toString() {
        return name + " " + surname;
    }

    public static final Comparator<Account> NAME_CASE_INSENSITIVE = new Comparator<Account>() {
        @Override
        public int compare(Account o1, Account o2) {
            return o1.getName().compareToIgnoreCase(o2.getName());
        }
    };
}

potem używasz np. osoby.sort(Osoba.NAME_CASE_INSENSITIVE); albo np. Collections.sort(osoby,Osoba.NAME_CASE_INSENSITIVE);

inna wersja tego co wrzucił @dymul:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

class Account {
    private String name, surname;

    public Account(String name, String surname) {
        this.name = name;
        this.surname = surname;
    }

    public String getName() {
        return name;
    }

    public String getSurname() {
        return surname;
    }

    @Override
    public String toString() {
        return name + " " + surname;
    }
}

enum AccountComparator implements Comparator<Account> {
    BY_NAME_CASE_INSENSITIVE {
        @Override
        public int compare(Account o1, Account o2) {
            return o1.getName().compareToIgnoreCase(o2.getName());
        }
    },

    BY_SURNAME_CASE_INSENSITIVE {
        @Override
        public int compare(Account o1, Account o2) {
            return o1.getSurname().compareToIgnoreCase(o2.getSurname());
        }
    };

    public static Comparator<Account> chain(final AccountComparator... comparators) {
        return new Comparator<Account>() {
            @Override
            public int compare(Account o1, Account o2) {
                for (AccountComparator ac : comparators) {
                    int result = ac.compare(o1, o2);
                    if (result != 0) {
                        return result;
                    }
                }
                return 0;
            }
        };
    }
}

public class Main {
    public static void main(String[] args) {
        List<Account> accounts = Arrays.asList(
                new Account("adamski", "gerg"),
                new Account("Zagłobie", "cccc"),
                new Account("Adamski", "GERG"),
                new Account("Adamski", "bebe")
        );
        accounts.sort(AccountComparator.chain(
                AccountComparator.BY_NAME_CASE_INSENSITIVE,
                AccountComparator.BY_SURNAME_CASE_INSENSITIVE
        ));
        System.out.println(accounts);
    }
}

lecz gdy mamy Java8, Guave to te rozwiązania są bez sensu.

0

To rozwiązania jest mało przydatne - źle sortuje polskie litery. Poniższe dobrze sortuje polskie litery i jest case insensitive

import java.text.Collator;

public class Osoba implements Comparable
{
    private String nazwisko = "";
    private String imie = "";
    public static int kryterium = 1;
    private static Collator kolator = Collator.getInstance();
    
    public Osoba(String nazwisko, String imie)
    {
        this.nazwisko = nazwisko;
        this.imie = imie;
    }
    @Override
    public int compareTo(Object ob)
    {
        Osoba osoba = (Osoba)ob;
        if(kryterium == 1)
        {
            return kolator.compare(this.nazwisko,osoba.nazwisko);
        }
        else
        {
            return kolator.compare(this.imie,osoba.imie);
        }
    }
    public String toString()
    {
        return imie+" "+nazwisko;
    }
}
0

Z dedykacją dla @karolinaa, w klasie Osoba

//pola
    public enum rules
    {
        SORTING_BY_NAME,SORTING_BY_SURNAME
    };
    private static rules rule = rules.SORTING_BY_NAME;
...
    public int compareTo(Object ob)
    {
        Osoba osoba = (Osoba)ob;
        if(rule == rules.SORTING_BY_NAME)
        {
            return kolator.compare(this.nazwisko,osoba.nazwisko);
        }
        else
        {
            return kolator.compare(this.imie,osoba.imie);
        }
    }
...
    public static void setSortingRule(rules kryterium)
    {
        rule = kryterium;
    }

Na zewnątrz tej klasy

Osoba.setSortingRule(Osoba.rules.SORTING_BY_SURNAME);
0

Może, żeby nie kombinować zaktualizowana do Javy 8 paczka podstawowych komparatorów sortujących po każdym alfabecie narodowym znanym przez JVM z uwzględnieniem poziomów szczegółowości Unikodu. I na końcu przykładowe wykorzystania w metodzie main z użyciem lambd i referencji do metod.
Kod do wykorzystania przez każdego (wywalony tylko javadoc). Korzysta wyłącznie ze standardowych klas.

package com.olamagato.util.text;

import java.text.CollationKey;
import java.text.Collator;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;

public class StringComparators
{
	public static <ELEMENT> Comparator<ELEMENT> compareIgnoreCase()
	{
		return compareIgnoreCase(Locale.getDefault());
	}

	public static <ELEMENT>
		Comparator<ELEMENT> compareIgnoreCase(Function<ELEMENT, String> by)
	{
		return new LocaleComparator<>(by, getInsensitiveCaseCollator(
			Locale.getDefault()));
	}

	public static <ELEMENT> Comparator<ELEMENT> compareIgnoreCase(Locale locale)
	{
		return new LocaleComparator<>(getInsensitiveCaseCollator(locale));
	}

	public static <ELEMENT> Comparator<ELEMENT>
		compareIgnoreCase(Function<ELEMENT, String> by, Locale locale)
	{
		return new LocaleComparator<>(by, getInsensitiveCaseCollator(locale));
	}

	public static <ELEMENT> Comparator<ELEMENT> compare()
	{
		return compare(Locale.getDefault());
	}

	public static <ELEMENT>
		Comparator<ELEMENT> compare(Function<ELEMENT, String> by)
	{
		return new LocaleComparator<>(by, getSensitiveCaseCollator(
			Locale.getDefault()));
	}

	public static <ELEMENT> Comparator<ELEMENT> compare(Locale locale)
	{
		return new LocaleComparator<>(getSensitiveCaseCollator(locale));
	}

	public static <ELEMENT>
		Comparator<ELEMENT> compare(Function<ELEMENT, String> by, Locale locale)
	{
		return new LocaleComparator<>(by, getSensitiveCaseCollator(locale));
	}

	//właściwy obiekt komparatora
	private static class LocaleComparator<ELEMENT>
		implements Comparator<ELEMENT>
	{
		private final Map<ELEMENT, CollationKey> cache = new HashMap<>();
		private final Collator collator;
		private final Function<ELEMENT, String> by;

		protected LocaleComparator(Collator collator)
		{
			this.collator = collator;
			this.by = ELEMENT::toString;
		}

		protected LocaleComparator(Function<ELEMENT, String> by,
			Collator collator)
		{
			this.collator = collator;
			this.by = by;
		}

		@Override public int compare(ELEMENT left, ELEMENT right)
		{
			CollationKey leftCollatorKey =
				cache.get(left), rightCollationKey = cache.get(right);
			if(leftCollatorKey == null)
			{
				leftCollatorKey = collator.getCollationKey(by.apply(left));
				cache.put(left, leftCollatorKey);
			}
			if(rightCollationKey == null)
			{
				rightCollationKey = collator.getCollationKey(by.apply(right));
				cache.put(right, rightCollationKey);
			}
			return leftCollatorKey.compareTo(rightCollationKey);
		}

	}

	private static Collator getSensitiveCaseCollator(Locale locale)
	{
		final Collator result = Collator.getInstance(locale);
		result.setStrength(Collator.SECONDARY);
		return result;
	}

	private static Collator getInsensitiveCaseCollator(Locale locale)
	{
		final Collator result = Collator.getInstance(locale);
		result.setStrength(Collator.TERTIARY);
		return result;
	}

	private StringComparators() {}

	@SuppressWarnings("UseOfSystemOutOrSystemErr")
	public static void main(String[] args)
	{
		class Osoba implements Comparable<Osoba>
		{
			public Osoba(String imię, String nazwisko, int wiek)
			{
				this.imię = imię;
				this.nazwisko = nazwisko;
				this.wiek = wiek;
			}
			@Override public int compareTo(Osoba other)
			{
				return this.imię.compareToIgnoreCase(other.imię);
			}
			@Override public String toString()
			{
				return imię + ' '+ nazwisko + ' ' + wiek + " lat";
			}
			public String getImię() { return imię; }
			public String getNazwisko() { return nazwisko; }
			public int getWiek() { return wiek; }

			private final String imię;
			private final String nazwisko;
			private final int wiek;
		}

		int wiek = 27;
		Osoba[] osoby =
		{
			new Osoba("Jan", "Kowalski", wiek += 3),
			new Osoba("Ola", "Englert", wiek += 3),
			new Osoba("Ola", "Łasica", wiek),
			new Osoba("Ryszard", "Ludwik", wiek -= 15),
			new Osoba("Tadeusz", "Nidzicki", wiek),
			new Osoba("Tadeusz", "Ówczesny", wiek),
			new Osoba("Kizia", "Mizia", wiek += 3),
			new Osoba("Jurek", "Pachoł", wiek -= 12),
			new Osoba("Jurek", "Łomnicki", wiek -= 2),
			new Osoba("Michał", "Jasienica", wiek += 3),
			new Osoba("Dariusz", "Ryk", wiek -= 8),
			new Osoba("Tomek", "Kowalski", wiek += 3),
		};

		System.out.println("Kolejność oryginalna:");
		Arrays.stream(osoby).forEachOrdered(osoba -> System.out.println(osoba));

		System.out.println("\nWg niczego oryginalnie:");
		Arrays.<Osoba>sort(osoby);
		Arrays.stream(osoby).forEachOrdered(osoba -> System.out.println(osoba));

		System.out.println("\nWg niczego zlokalizowane:");
		Arrays.<Osoba>sort(osoby, StringComparators.compareIgnoreCase());
		Arrays.stream(osoby).forEachOrdered(osoba -> System.out.println(osoba));

		System.out.println("\nWg imienia i nazwiska:");
		Arrays.<Osoba>sort(osoby, StringComparators
			.compare(osoba -> osoba.imię + " " + osoba.nazwisko));
		Arrays.stream(osoby).forEachOrdered(osoba -> System.out.println(osoba));

		System.out.println("\nWg nazwisk:");
		Arrays.<Osoba>sort(osoby, StringComparators
			.compare(Osoba::getNazwisko));
		Arrays.stream(osoby).forEachOrdered(osoba -> System.out.println(osoba));

		System.out.println("\nWg wieku i imienia:");
		Arrays.<Osoba>sort(osoby, StringComparators.compare(osoba -> String
			.format("%02d%s", osoba.wiek, osoba.imię))
		);
		Arrays.stream(osoby).forEachOrdered(osoba -> System.out.println(osoba));
	}
}

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