Java fundamentals through coding exercises
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

10 KiB

Yhteystiedot ja osoitekirja 3/3 - Yhteystietojen järjestäminen aakkosjärjestykseen

Tässä tehtävässä sinun tehtäväsi on järjestää yhteystiedot nimien mukaiseen aakkosjärjestykseen. Tämä tehtävä on ohjelmointi 1 -kurssin näkökulmasta edistynyttä lisäsisältöä, minkä vuoksi se on julkaistu bonustehtävänä.

Taustatietoa

Jos yritämme järjestää oman Contact-luokan oliot Collections.sort-metodin avulla, saamme seuraavan virheilmoituksen:

"The method sort(List) in the type Collections is not applicable for the arguments (List)"

Tämä johtuu siitä, että luokkamme ei ole yhteensopiva Comparable-tyypin kanssa. Collections.sort-metodi ei siis pysty vertailemaan olioitamme keskenään ilman, että määrittelemme sille lisäksi käytettävän vertailuoperaation.

Tutustu Javan Comparator.comparing-metodiin, jonka avulla voit määritellä vertailijan kutsumaan mitä tahansa oman luokkasi metodia olioiden järjestämiseksi: https://www.baeldung.com/java-8-comparator-comparing. Tällä kurssilla sinun kannattaa lukea artikkelista kohta 3.1. Key Selector Variant ja sitä aikaisemmat kappaleet, mutta ei välttämättä tätä kappaletta pidemmälle.

Kuten artikkelissa kerrotaan, Comparator.comparing-metodille voidaan antaa metodiviittaus mihin tahansa metodiin. Collections.sort käyttää tällöin vertailussa juuri haluamasi metodin palauttamia arvoja, joita vertaillaan keskenään. Voisimme siis esimerkiksi järjestää merkkijonot pituusjärjestykseen vertailemalla merkkijonojen pituuksia, jotka selviävät length()-metodin avulla: Comparator.comparing(String::length).

Collections.sort käyttää tässä esimerkissä järjestelemiseen Comparator-oliota, joka vertaa merkkijonojen pituuksia toisiinsa:

List nesteet = Arrays.asList("maito", "Vesi", "ketsuppi", "bensa", "Limu");
// tehdään järjestely merkkijonojen length-metodin mukaan:
Collections.sort(nesteet, Comparator.comparing(String::length));
System.out.println(nesteet); // lyhimmästä merkkijonosta pisimpään: [Limu, Vesi, bensa, maito, ketsuppi]

Tehtävä

Jatkokehitä nyt edellisissä tehtävissä käyttämiäsi kolmea osoitekirja- ja yhteystietoluokkaa siten, että osoitekirjan sisältöä listattaessa "list"-komennolla yhteystiedot esitetään aakkosjärjestyksessä nimen mukaan.

Sinun kannattaa hyödyntää edellä käsiteltyä Collections.sort-metodia sekä Comparator.comparing-metodia siten, että annat comparing-metodille metodiviittauksen Contact-luokkasi getName-metodiin.

ESIMERKKI

This is an address book application. Available commands:
list
help
add <name>, <email>, <phone>
search <name>
exit
> add Maija Meikäläinen, maija@example.com, +35850555556
Added Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
> add Matti Meikäläinen, matti@example.com, +35850555555
Added Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
> add Benjamin Meikäläinen, benjamin@example.com, +35850555557
Added Benjamin Meikäläinen (email: benjamin@example.com, phone: +35850555557)
> add Abraham Meikäläinen, abraham@example.com, +35850555558
Added Abraham Meikäläinen (email: abraham@example.com, phone: +35850555558)
> add Wolverine Meikäläinen, wolverine@example.com, +35850555559
Added Wolverine Meikäläinen (email: wolverine@example.com, phone: +35850555559)
> list
Abraham Meikäläinen (email: abraham@example.com, phone: +35850555558)
Benjamin Meikäläinen (email: benjamin@example.com, phone: +35850555557)
Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
Wolverine Meikäläinen (email: wolverine@example.com, phone: +35850555559)
> exit
Bye!

AddressBookApp.java

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class AddressBookApp {
private static String welcomeText = "This is an address book application. Available commands:\n";
private static String commandMenu = " list\n" +
" help\n" +
" add <name>, <email>, <phone>\n" +
" search <name>\n" +
" exit\n";
// Keep here instead of translating to a local variable
private static String inputCLIPrefix  = "> ";
// Keep added addressBook values during application runtime
private static AddressBook addressBook = new AddressBook();
public static void main(String[] args) {
System.out.print(welcomeText + commandMenu);
while (true) {
runApp();
}
}
private static void runApp() {
Scanner inputPrompt    = new Scanner(System.in);
Object[] getInput      = parseInput(inputCLIPrefix, inputPrompt);
String inputCmd        = String.valueOf(getInput[0]);
// TODO Avoid risk of ClassCastException
List<String> inputArgs = (ArrayList<String>)getInput[1];
int argsCount          = 3;
switch(inputCmd) {
case "list":
System.out.println(addressBook.toString());
break;
case "help":
System.out.print(welcomeText + commandMenu);
break;
case "add":
// TODO Should this be checked in AddressBook.add method instead?
if (inputArgs.size() != argsCount) {
System.err.printf("Invalid count of input arguments (expected %d).\n", argsCount);
break;
}
String name  = String.valueOf(inputArgs.get(0));
String email = String.valueOf(inputArgs.get(1));
String phone = String.valueOf(inputArgs.get(2));
Contact contact = new Contact(name, email, phone);
// NOTE: Else condition not needed as 3 arguments is always passed to this object method call
if(addressBook.add(contact)) {
System.out.printf("Added %s\n", contact.toString());
}
break;
case "search":
String searchTerm  = String.valueOf(inputArgs.get(0));
Contact match      = addressBook.search(searchTerm);
if (match == null) {
System.out.printf("%s does not match any contact.\n", searchTerm);
} else {
System.out.println(match.toString());
}
break;
case "exit":
System.out.print("Bye!\n");
System.exit(0);
default:
System.err.println("Command not found.");
//break;
}
}
private static Object[] parseInput(String prefix, Scanner inputRaw) {
System.out.print(prefix);
String inputR = inputRaw.nextLine();
String command   = inputR.split(" ")[0];
String[] theRest = inputR.split(",");
theRest[0] = theRest[0].replaceAll("^\\s*" + command + "\\s*","");
List<String> arguments = new ArrayList<String>();
for (int i = 0; i < theRest.length; i++) {
arguments.add(theRest[i].trim());
}
Object[] parsedOutput = new Object[2];
parsedOutput[0] = command;
parsedOutput[1] = arguments;
return parsedOutput;
}
}

AddressBook.java

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class AddressBook {
private List<Contact> contacts;
public AddressBook() {
this.contacts = new ArrayList<>();
}
public boolean add(Contact newContact) {
// Create a temporary ArrayList, if we have contacts already
// Get list of all contacts, put them into the new ArrayList contactsTemp
// Loop through all contacts in contactsTemp
// Check the current contact in contactsTemp against newContact
if (this.contacts.size() > 0) {
List<Contact> contactsTemp = new ArrayList<>(this.contacts);
for (Contact tempContact : contactsTemp) {
if (tempContact.equals(newContact)) {
System.out.println("That contact already exists.");
return false;
}
}
}
this.contacts.add(newContact);
return true;
}
public Contact search(String keyword) {
for (Contact current : this.contacts) {
String name = current.getName();
if (name != null && name.toLowerCase().contains(keyword.toLowerCase())) {
return current; // palautetaan löytynyt arvo heti
}
}
return null; // palautetaan null, jos ei löytynyt
}
@Override
public String toString() {
String returnString = "";
// Sort by contact names. Define new Comparator object
Collections.sort(this.contacts, new Comparator<Contact>() {
public int compare(Contact contact1, Contact contact2) {
return contact1.getName().compareTo(contact2.getName());
}
});
for (Contact contact : this.contacts) {
returnString += contact + "\n";
}
return returnString;
}
}

Contact.java

public class Contact {
private String name;
private String email;
private String phone;
// NOTE: This does not validate or check input String values
// (Is name actually a name, email a valid email, and phone number an actual phone number?)
public Contact(String name, String email, String phone) {
this.name = name;
this.email = email;
this.phone = phone;
}
public String getName() {
return this.name;
}
@Override
public boolean equals(Object contact) {
if (this == contact) { return true; }
if (!(this instanceof Contact)) { return false; }
Contact contactT = (Contact) contact;
if (this.name.equals(contactT.name) && this.email.equals(contactT.email) && this.phone.equals(contactT.phone)) {
return true;
}
return false;
}
public String toString() {
// Haluttu muoto: "Maija Meikäläinen (email: foo@bar.fi, phone: 5555)"
return this.name + " (email: " + this.email + ", phone: " + this.phone + ")";
}
}