Sorting of two ArrayList/List Objects - java

For example, I have two Arrays converted into ArrayList which is firstName and lastName. I want to sort these two lists using the first names, the last name will follow through the first names.
Expected output:
firstNameList = {Andrew, Johnson, William}
lastNameList = {Wiggins, Beru, Dasovich};
My Initial Program:
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
String [] firstName = {William, Johnson, Andrew};
String [] lastName = {Dasovich, Beru, Wiggins};
//Will convert arrays above into list.
List <String> firstNameList= new ArrayList<String>();
List <String> lastNameList= new ArrayList<String>();
//Conversion
Collections.addAll(firstNameList, firstName);
Collections.addAll(lastNameList, lastName);

Domain
As I have stated in my comment, I would recommend using a Person-POJO to bind firstName and lastName in a semantic way:
class Person {
public static final String PERSON_TO_STRING_FORMAT = "{f: %s, l: %s}";
private final String firstName;
private final String lastName;
private Person(final String firstName, final String lastName) {
this.firstName = Objects.requireNonNull(firstName);
this.lastName = Objects.requireNonNull(lastName);
}
public static Person of(final String firstName, final String lastName) {
return new Person(firstName, lastName);
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
#Override
public String toString() {
return String.format(PERSON_TO_STRING_FORMAT, getFirstName(), getLastName());
}
}
To convert two String[]s firstNames and lastNames into a List<Person>, one can provide a method:
public static List<Person> constructPersons(
final String[] firstNames,
final String[] lastNames) {
if (firstNames.length != lastNames.length) {
throw new IllegalArgumentException("firstNames and lastNames must have same length");
}
return IntStream.range(0, firstNames.length)
.mapToObj(index -> Person.of(firstNames[index], lastNames[index]))
.collect(Collectors.toCollection(ArrayList::new));
}
A remark on this method: Here, we use collect(Collectors.toCollection(...)) instead of collect(Collectors.toList()) to have some control with respect to list mutability since we are going to sort the list.
From here on there are two general routes: Either one makes Person comparable by public class Person implements Comparable<Person> or one writes a Comparator<Person>. We will discuss both possibilities.
Challenge
The goal is to sort Person-objects. The primary criteria for sorting is the first name of the person. If two persons have equal first names, then they should be ordered by their last names. Both first- and last name are String-objects and should be ordered in lexicographical order, which is String's natural order.
Solution 1: Implementing Comparable<Person> on Person
The logic to implement the comparison is straight-forward:
Compare the firstNames of two persons using equals(...).
If they are equal, compare the lastNames using compareTo(...) and return the result.
Otherwise, compare the firstNames with compareTo(...) and return the result.
The corresponding method would then look like this:
public class Person implements Comparable<Person> {
...
#Override
public final int compareTo(final Person that) {
if (Objects.equals(getFirstName(), that.getFirstName())) {
return getLastName().compareTo(that.getLastName());
}
return getFirstName().compareTo(that.getFirstName());
}
...
}
While not strictly necessary, it is recommended that the natural ordering of a class (i.e. the Comparable-implementation) is consistent with its equals(...)-implementation. Since this is not the case right now, I would recommend overriding equals(...) and hashCode():
public class Person implements Comparable<Person> {
...
#Override
public final boolean equals(Object thatObject) {
if (this == thatObject) {
return true;
}
if (thatObject == null || getClass() != thatObject.getClass()) {
return false;
}
final Person that = (Person) thatObject;
return Objects.equals(getFirstName(), that.getFirstName()) &&
Objects.equals(getLastName(), that.getLastName());
}
#Override
public final int hashCode() {
return Objects.hash(getFirstName(), getLastName());
}
...
}
The following code demonstrates how to create and order a List<Person> from two String[]:
final List<Person> persons = constructPersons(
new String[]{"Clair", "Alice", "Bob", "Alice"},
new String[]{"Clear", "Wonder", "Builder", "Ace"}
);
Collections.sort(persons);
System.out.println(persons);
Solution 2: Implementing a Comparator<Person>
A traditional implementation of a comparator realizing the sort comparison given in the challenge-section may look like this:
class PersonByFirstNameThenByLastNameComparator implements Comparator<Person> {
public static final PersonByFirstNameThenByLastNameComparator INSTANCE =
new PersonByFirstNameThenByLastNameComparator();
private PersonByFirstNameThenByLastNameComparator() {}
#Override
public int compare(final Person lhs, final Person rhs) {
if (Objects.equals(lhs.getFirstName(), rhs.getFirstName())) {
return lhs.getLastName().compareTo(rhs.getLastName());
}
return lhs.getFirstName().compareTo(rhs.getFirstName());
}
}
A example call may look like this:
final List<Person> persons = constructPersons(
new String[]{"Clair", "Alice", "Bob", "Alice"},
new String[]{"Clear", "Wonder", "Builder", "Ace"}
);
persons.sort(PersonByFirstNameThenByLastNameComparator.INSTANCE);
System.out.println(persons);
With Java 8, the construction of a Comparator has been simplified through the Comparator.comparing-API. To define a Comparator realizing the order given in section Challenge with the Comparator.comparing-API, we only need one line of code:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
The following code demonstrates how this Comparator is used to sort a List<Person>:
final List<Person> persons = constructPersons(
new String[]{"Clair", "Alice", "Bob", "Alice"},
new String[]{"Clear", "Wonder", "Builder", "Ace"}
);
persons.sort(Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName));
System.out.println(persons);
Closing Notes
A MRE is available on Ideone.
I would question the initial design decision to split up first- and last names into two separate arrays. I opted to not include the method List<Person> constructPersons(String[] firstNames, String[] lastNames) in class Person since this is just adapter-code. It should be contained in some mapper, but is not a functionality that is existential for Person.

You can do this by merging the two arrays into one stream of Names, with first and last name, sort that stream and then recreate the two lists.
String[] firstName = {"William", "Johnson", "Andrew"};
String[] lastName = {"Dasovich", "Beru", "Wiggins"};
final var sortedNames = IntStream.range(0, firstName.length)
.mapToObj(i -> new Name(firstName[i], lastName[i]))
.sorted(Comparator.comparing(n -> n.firstName))
.collect(Collectors.toList());
final var sortedFirstNames = sortedNames.stream()
.map(n -> n.firstName)
.collect(Collectors.toList());
final var sortedLastNames = sortedNames.stream()
.map(n -> n.lastName)
.collect(Collectors.toList());

As highlighted by comments , your problem is you are using two different lists for names and surnames so the ordering process for the two types of data are totally unrelated. A possible solution is creation of a new class Person including two fields name and surname and implementing Comparable interface like below:
public class Person implements Comparable<Person> {
public String firstName;
public String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public String toString() {
return "Person [firstName=" + firstName + ", lastName=" + lastName + "]";
}
#Override
public int compareTo(Person o) {
return this.firstName.compareTo(o.firstName);
}
public static void main(String[] args) {
Person[] persons = { new Person("William", "Dasovich"),
new Person("Johnson", "Beru"),
new Person("Andrew", "Wiggins") };
Collections.sort(Arrays.asList(persons));
for (Person person : persons) {
System.out.println(person);
}
}
}
The Collections.sort method provides the order of the Person array by firstName.

Because the firstName and lastName are connected to each other, you should create a class to model them as such. Let's call this class Person:
class Person {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
// Add toString, equals and hashCode as well.
}
Now, create a list of persons instead:
List<Person> persons = Arrays.asList(
new Person("Andrew", "Wiggins"),
new Person("Johnson", "Beru"),
new Person("William", "Dasovich"));
Now, to sort it, you can use the sorted method on a stream with a comparator. This will create a new List<Person> which will be sorted. The Comparator.comparing function will let you pick which property of the Person class that you want to sort on. Something like this:
List<Person> sortedPersons = persons.stream()
.sorted(Comparator.comparing(Person::getFirstName))
.collect(Collectors.toList());

A TreeSet could do it:
(using a Person class as suggested by Turing85)
import java.util.Set;
import java.util.TreeSet;
public class PersonTest {
private static class Person implements Comparable<Person> {
private final String firstName;
private final String lastName;
public Person(final String firstName, final String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public int compareTo(final Person otherPerson) {
return this.firstName.compareTo(otherPerson.firstName);
}
#Override
public String toString() {
return this.firstName + " " + this.lastName;
}
}
public static void main(final String[] args) {
final Set<Person> people = new TreeSet<>();
/**/ people.add(new Person("William", "Dasovich"));
/**/ people.add(new Person("Johnson", "Beru"));
/**/ people.add(new Person("Andrew", "Wiggins"));
people.forEach(System.out::println);
}
}
but Streams & a somewhat simpler Person class might do too:
import java.util.stream.Stream;
public class PersonTest {
private static class Person {
private final String firstName;
private final String lastName;
public Person(final String firstName, final String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public String toString() {
return this.firstName + " " + this.lastName;
}
}
public static void main(final String[] args) {
Stream.of(
new Person("William", "Dasovich"),
new Person("Johnson", "Beru" ),
new Person("Andrew", "Wiggins" ) )
.sorted ((p1,p2) -> p1.firstName.compareTo(p2.firstName))
.peek (System.out::println)
.sorted ((p1,p2) -> p1.lastName .compareTo(p2.lastName))
.forEach(System.out::println);
}
}

String[] firstName = {"William", "Johnson", "Andrew"};
String[] lastName = {"Dasovich", "Beru", "Wiggins"};
// combine the 2 arrays and add the full name to an Array List
// here using a special character to combine, so we can use the same to split them later
// Eg. "William # Dasovich"
List<String> combinedList = new ArrayList<String>();
String combineChar = " # ";
for (int i = 0; i < firstName.length; i++) {
combinedList.add(firstName[i] + combineChar + lastName[i]);
}
// Sort the list
Collections.sort(combinedList);
// create 2 empty lists
List<String> firstNameList = new ArrayList<String>();
List<String> lastNameList = new ArrayList<String>();
// iterate the combined array and split the sorted names to two lists
for (String s : combinedList) {
String[] arr = s.split(combineChar);
firstNameList.add(arr[0]);
lastNameList.add(arr[1]);
}
System.out.println(firstNameList);
System.out.println(lastNameList);

If you don't want to create DTO to keep the first names and last names together, you can use a kind of functional way based on java streams :
create couples with lists to bind those two values
sort them, base on the first name
flat the couple, in order to have a list with one dimension
String[] firstName = {"William", "Johnson", "Andrew"};
String[] lastName = {"Dasovich", "Beru", "Wiggins"};
//Will convert arrays above into list.
List<String> firstNameList = new ArrayList<String>();
List<String> lastNameList = new ArrayList<String>();
//Conversion
Collections.addAll(firstNameList, firstName);
Collections.addAll(lastNameList, lastName);
List<String> collect = firstNameList
.stream()
.map(name -> {
List<String> couple = List.of(name, lastNameList.get(0));
lastNameList.remove(0);
return couple;
})
.sorted(Comparator.comparing(l -> l.get(0)))
.flatMap(Collection::stream)
.collect(Collectors.toList());

String[] firstNames = {William, Johnson, Andrew};
String[] lastNames = {Dasovich, Beru, Wiggins};
//Will convert arrays above into list.
List<String> firstNameList = new ArrayList<String>();
List<String> lastNameList = new ArrayList<String>();
Map<String, String> lastNameByFirstName = new HashMap<>();
for (int i = 0; i < firstNames.length; i++) {
lastNameByFirstName.put(firstNames[i], lastNames[i]);
}
//Conversion
Collections.addAll(firstNameList, firstNames);
Collections.sort(firstNameList);
for (String firstName : firstNameList) {
lastNameList.add(lastNameByFirstName.get(firstName));
}

Related

Data structure for storing phonebook data

What would be the best data structure to store phone book contacts, each consisting of first name, last name and phone number. The user must be able to search by each one of the fields.
There has been similar questions, but none of the answers were clear enough.
Create a POJO type, that stores first name, last name, and phone number (could make it mutable if needed).
class PhoneBookEntry {
public final String firstName;
public final String lastName;
public final String phoneNumber;
public Entry(String firstName, String lastName, String phoneNumber) {
this.firstName = firstName;
this.lastName = lastName;
this.phoneNumber = phoneNumber;
}
//... equals + hashcode implementation
}
You can create your phone book like this:
class PhoneBook {
private Map<String, Set<PhoneBookEntry>> firstNameMap;
private Map<String, Set<PhoneBookEntry>> lastNameMap;
private Map<String, Set<PhoneBookEntry>> phoneNumberMap;
public void add(PhoneBookEntry entry) {
Set<PhoneBookEntry> set
= firstNameMap.computeIfAbsent(entry.firstName, k -> new HashSet<>());
set.add(entry);
set = lastNameMap.computeIfAbsent(entry.lastName, k -> new HashSet<>());
set.add(entry);
set = phoneNumberMap.computeIfAbsent(entry.phoneNumber, k -> new HashSet<>());
set.add(entry);
}
public Set<PhoneBookEntry> getByFirstName(String firstName) {
return firstNameMap.get(firstName);
}
public Set<PhoneBookEntry> getByLastName(String lastName) {
return lastNameMap.get(lastName);
}
public Set<PhoneBookEntry> getByPhoneNumber(String phoneNumber) {
return phoneNumberMap.get(phoneNumber);
}
}
Using Maps allows for fast lookup.
As yitzih said, Multiple contacts can have the same first name, last name, or phone number. So a lookup by first name (for instance), will return a set of contacts.
Create a Contact object that stores the variables needed for each contact. Use an ArrayList to store them.
Without having more information about the contact there isn't really any way to use a HashTable, Map or Graph. There is no real key value pair for a HashTable unless you want to use a combination of first and last names, but you would need some way to handle conflicts (if 2 people have the exact same name.), or you would need to forbid having 2 people having the same Contact name (but why would you want to do that?)
Class Contact{
String forename;
String Surname;
String phoneNo;
public Contact(fName, sName, pNo){
forename = fName;
Surname = sName;
phoneNo = pNo;
}
public String getForename(){}
public String getSurname(){}
public String getPhoneNo(){}
}
in the class handling the search,
you declare an arrayList of type Contact, and when searching for a contact say John,
public Contact searchContact(String s){
for(int i = 0; i< ContactList.size(); i++){
if(ContactList.get(i).getForename().equals(s) ||
ContactList.get(i).getSurame().equals(s) ||
ContactList.get(i).getPhoneNo().equals(s)
){
return ContactList.get(i);
}
}
return null;
}
Kind of a vague question, but what the heck, maybe this'll chase away my post-lunch sleepies. I'm assuming a simple String representation of the phone number, but the best data object to store all the possible varieties of world phone numbers along with a method to intelligently search them (e.g. is "(123) 456-7891" the same as "1234567891"?) could be it's own question entirely.
Here a PhoneBook class stores all of the contacts. The methods searchFirst(), searchLast() and searchPhoneNumber() each return lists of matching contacts.
public class PhoneBook {
ArrayList<Contact> contacts;
public PhoneBook() {
contacts = new ArrayList<>();
}
public void addContact(Contact contact) {
contacts.add(contact);
}
public ArrayList<Contact> searchFirst(String first) {
ArrayList<Contact> foundContacts = new ArrayList<>();
for (Contact contact: contacts) {
if (contact.first.equals(first)) {
foundContacts.add(contact);
}
}
return foundContacts;
}
public ArrayList<Contact> searchLast(String last) {
ArrayList<Contact> foundContacts = new ArrayList<>();
for (Contact contact: contacts) {
if (contact.last.equals(last)) {
foundContacts.add(contact);
}
}
return foundContacts;
}
public ArrayList<Contact> searchPhoneNumber(String phoneNumber) {
ArrayList<Contact> foundContacts = new ArrayList<>();
for (Contact contact: contacts) {
if (contact.phoneNumber.equals(phoneNumber)) {
foundContacts.add(contact);
}
}
return foundContacts;
}
class Contact {
String first;
String last;
String phoneNumber;
public Contact(String first, String last, String phoneNumber) {
this.first = first;
this.last = last;
this.phoneNumber = phoneNumber;
}
}
}

Sort Array of Persons with sort method

So I am to create this program that creates an array of persons which is sorted by a method in the class Algorithms. I am to create the interface Sortable which defines a comparison method called compareTo which should compare 2 objects to see which comes first. The Person Class represents a person and implements Sortable, and the Algorithms class has a method named sort which takes an array consisting of Sortable objects (Persons) and sort these. I am stuck, and my coursebook is not helping me much here.
public interface Sortable <T> {
int compareTo(T ob);
}
.
public class Algorithms implements Sortable <Person>{
public int compare(Person p1, Person p2){
return p1.lastName().compareTo(p2.lastName());
}
}
.
public class Person implements Sortable<Person>
{
String firstName;
String lastName;
String dob;
public Person (String lastName, String firstName, String dob){
this.lastName=lastName;
this.firstName=firstName;
this.dob=dob;
}
public String lastName(){
return lastName;
}
public String firstName(){
return firstName;
}
public String dob(){
return dob;
}
#Override
public int compareTo(Person o){
Person p = (Person)o;
int last = lastName.compareTo(o.lastName);
return last;
}
public String toString(){
return "Namn "+ lastName +" "+ firstName +" Personnummer: "+dob;
}
}
.
public class Personer {
public static void main(String[]args){
Person p1 = new Person ("Ek","Ida","530525-0055") ;
Person p2 = new Person ("Björk","Sten","650203-0250");
Person p3 = new Person ("Al", "Bengt","881212-4455");
List <Person> list = new ArrayList<>();
list.add (p1);
list.add (p2);
list.add (p3);
Arrays.sort(list, new Algorithms());
System.out.println("lista: "+list);
}
}
The question is really what do I need to do to make this code do what I want it to do, which in the end is to print out the names and dob of a number of people in alphabetical order based om last name
You Shall implement Comparable on your Person Class:
public class Person implements Comparable<Person> {
String firstName;
String lastName;
String dob;
public Person(String lastName, String firstName, String dob) {
this.lastName = lastName;
this.firstName = firstName;
this.dob = dob;
}
public String lastName() {
return lastName;
}
public String firstName() {
return firstName;
}
public String dob() {
return dob;
}
#Override
public int compareTo(Person o) {
int last = lastName.compareTo(o.lastName);
return last;
}
public String toString() {
return "Namn " + lastName + " " + firstName + " Personnummer: " + dob;
}
}
And use Collections#sort method to sort list
public class Personer {
public static void main(String[] args) {
Person p1 = new Person("Ek", "Ida", "530525-0055");
Person p2 = new Person("Björk", "Sten", "650203-0250");
Person p3 = new Person("Al", "Bengt", "881212-4455");
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
Collections.sort(list);
System.out.println("lista: " + list);
}
}
These two classes are only required.
Modify Algorithms class and implement Comparator interface
import java.util.Comparator;
public class Algorithms implements Comparator <Person>{
public int compare(Person p1, Person p2){
return p1.lastName().compareTo(p2.lastName());
}
}
For sorting (in Personer class) use
List <Person> list = new ArrayList<>();
list.add (p1);
list.add (p2);
list.add (p3);
list.sort(new Algorithms());
You can use the sort method available in Collections. Use an anonymous inner class to create a new Comparator which compares on the lastName if you don't want to limit the compareTo in your Person class to only compare on lastName.
Collections.sort(list, new Comparator<Person>() {
#Override
public int compare(Person o1, Person o2) {
return o1.lastName().compareTo(o2.lastName());
}
});

Java 8 groupingby with custom key

I have a series of input Strings in the following format:
typeA:code1,
typeA:code2,
typeA:code3,
typeB:code4,
typeB:code5,
typeB:code6,
typeC:code7,
...
and I need to get a Map<String, List<String>> with the following structure:
typeA, [code1, code2, code3]
typeB, [code4, code5, code6]
typeC, [code7, code8, ...]
The catch is that to generate each type I need to call a function like this one on each input String:
public static String getType(String code)
{
return code.split(":")[0]; // yes this is horrible code, it's just for the example, honestly
}
I'm pretty confident that Streams and Collectors can do this, but I'm struggling to get the right incantation of spells to make it happen.
The code becomes simple if you consider what you have omitted, that you need the second part of the split string as well:
Map<String, List<String>> result = Stream.of(input).map(s->s.split(":", 2))
.collect(groupingBy(a->a[0], mapping(a->a[1], toList())));
(assuming you have a import static java.util.stream.Collectors.*;)
There is nothing wrong with splitting a String into an array, the implementation has even a “fast-path” for the common case you are splitting using a single simple character instead of a complicate regular expression.
Here is one way to do it (assuming the class is named A):
Map<String, List<String>> result = Stream.of(input)
.collect(groupingBy(A::getType, mapping(A::getValue, toList())));
If you want the output sorted you can use a TreeMap instead of the default HashMap:
.collect(groupingBy(A::getType, TreeMap::new, mapping(A::getValue, toList())));
Full example:
public static void main(String[] args) {
String input[] = ("typeA:code1," +
"typeA:code2," +
"typeA:code3," +
"typeB:code4," +
"typeB:code5," +
"typeB:code6," +
"typeC:code7").split(",");
Map<String, List<String>> result = Stream.of(input)
.collect(groupingBy(A::getType, mapping(A::getValue, toList())));
System.out.println(result);
}
public static String getType(String code) {
return code.split(":")[0];
}
public static String getValue(String code) {
return code.split(":")[1];
}
Although I was too slow, here is an MCVE showing how this can be solved with Collectors#groupingBy.
There are obviously different options for defining the "classifier" and "mapper". Here I'm simply using String#substring to find the part before and after the ":".
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class GroupingBySubstringsTest
{
public static void main(String[] args)
{
List<String> strings = new ArrayList<String>();
strings.add("typeA:code1");
strings.add("typeA:code2");
strings.add("typeA:code3");
strings.add("typeB:code4");
strings.add("typeB:code5");
strings.add("typeB:code6");
strings.add("typeC:code7");
Map<String, List<String>> result = strings.stream().collect(
groupingBy(s -> s.substring(0, s.indexOf(":")),
mapping(s -> s.substring(s.indexOf(":")+1), toList())));
for (Entry<String, List<String>> entry : result.entrySet())
{
System.out.println(entry);
}
}
}
Consider Student Class:
public Student(String name, Address add) {
super();
this.name = name;
this.add = add;
}
private String name;
private Address add;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAdd() {
return add;
}
public void setAdd(Address add) {
this.add = add;
}
}
And Address class:
class Address{
public Address(String city, String state) {
super();
this.city = city;
State = state;
}
private String city;
private String State;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return State;
}
public void setState(String state) {
State = state;
}
}
Now, if I want to group Student based on City & State which is part of Address class:
Student s1 = new Student("Rohit", new Address("Mumbai", "MH"));
Student s2 = new Student("Sudeep", new Address("Mumbai", "MH"));
Student s3 = new Student("Amit", new Address("Pune", "MH"));
Student s4 = new Student("Rahul", new Address("Blore", "KR"));
Student s5 = new Student("Vishal", new Address("Blore", "KR"));
List<Student> st = Arrays.asList(s1,s2,s3,s4,s5);
Function<Student, String> compositeKey = studRecord -> studRecord.getAdd().getCity()+":"+ studRecord.getAdd().getState();
Map<String, List<Student>> groupedStudent = st.stream()
.collect(Collectors.groupingBy(compositeKey));

Setting Variable in Class that Extends other class

the psuedo for what im trying to do is
-send Array of EMPLOYEE objects to Restaurant Class
-In Class RESTAURANT give each of the employee objects a name and last name (last name not in employee Class but in PERSON Class which Employee CLass Extends.
-print say employeeList[1].getLastName()
hopefully my code explains better
class Person {
public Person(final String last) {
}
}
class Employee extends Person {
private String firstName;
// getFirstName method
// getLastName Method
Employee(final String first, final String last) {
super(last);
}
}
class Restaurant { // set first object in array have first and last name
public void setFirstLast(final Employee[] employeeList) {
String firstName = "Jovana";
String lastName = "Valdez";
employeeList[0] = new Employee(firstName, lastName); // set up via constructor
}
}
public class Main {
private String lastName;
public static void main(final String[] args) {
Employee[] employeeList = new Employee[1]; // my array of Employee objects, all set to null
Restaurant restaurant = new Restaurant();
restaurant.setFirstLast(employeeList);
}
}
from main when i try to print System.out.printf("first is %d\n",arrayList.getFirst()); i get null for the value as well as the value for the last name so what is the correct way to go about and set values to objects in the array?
Edit arrayList initialized in Class restaurant by
public Table[] create_table_array(Table table,int number) {
Table[] TableList = new Table[number];
int i = 0;
for(i = 0; i < number; i++) {
TableList[i] = table;
}
return TableList;
Your constructor doesn't save firstName, it should look like:
Employee(String first, String last) {
super(last);
firstName = first;
}
You did not make good constructor of Person class and it class does not have instance variable lastName in which you should assign value you get in constructor as a parameter.
Also constructor of Employee does not assign any value to firstName.
What ArrayList ?As i see you are working with arrays?I didn't see it in code anywhere?
System.out.printf("first is %d\n",**arrayList**.getFirst());so command is wrong.
Any code that has meaning to me and can be compilled is to fix those things and delete formatting options you putted in System.out.printf because you are not formatting numbers.
So code look like :
class Person {
String lastName;
public Person(final String last) {
lastName=last;
}
}
class Employee extends Person {
private String firstName;
public String getFirstName()
{return firstName;}
public String getLastName()
{return lastName;}
Employee(final String first, final String last) {
super(last);
firstName=first;
}
}
class Restaurant { // set first object in array have first and last name
public void setFirstLast(final Employee[] employeeList) {
String firstName = "Jovana";
String lastName = "Valdez";
employeeList[0] = new Employee(firstName, lastName); // set up via constructor
}
}
public class Main {
private String lastName;
public static void main(final String[] args) {
Employee[] employeeList = new Employee[1];
Restaurant restaurant = new Restaurant();
restaurant.setFirstLast(employeeList);
System.out.printf("first is "+employeeList[0].getFirstName()+" "+employeeList[0].getLastName());
}
}

Sorting in Hash Maps

I'm trying to get familiar with Collections. I have a String which is my key, email address, and a Person object (firstName, lastName, telephone, email). I read in the Java collections chapter on Sun's webpages that if you had a HashMap and wanted it sorted, you could use a TreeMap. How does this sort work? Is it based on the compareTo() method you have in your Person class? I overrode the compareTo() method in my Person class to sort by lastName. But it isn't working properly and was wondering if I have the right idea or not. getSortedListByLastName at the bottom of this code is where I try to convert to a TreeMap. Also, if this is the correct way to do it, or one of the correct ways to do it, how do I then sort by firstName since my compareTo() is comparing by lastName.
import java.util.*;
public class OrganizeThis
{
/**
Add a person to the organizer
#param p A person object
*/
public void add(Person p)
{
staff.put(p.getEmail(), p);
//System.out.println("Person " + p + "added");
}
/**
* Remove a Person from the organizer.
*
* #param email The email of the person to be removed.
*/
public void remove(String email)
{
staff.remove(email);
}
/**
* Remove all contacts from the organizer.
*
*/
public void empty()
{
staff.clear();
}
/**
* Find the person stored in the organizer with the email address.
* Note, each person will have a unique email address.
*
* #param email The person email address you are looking for.
*
*/
public Person findByEmail(String email)
{
Person aPerson = staff.get(email);
return aPerson;
}
/**
* Find all persons stored in the organizer with the same last name.
* Note, there can be multiple persons with the same last name.
*
* #param lastName The last name of the persons your are looking for.
*
*/
public Person[] find(String lastName)
{
ArrayList<Person> names = new ArrayList<Person>();
for (Person s : staff.values())
{
if (s.getLastName() == lastName) {
names.add(s);
}
}
// Convert ArrayList back to Array
Person nameArray[] = new Person[names.size()];
names.toArray(nameArray);
return nameArray;
}
/**
* Return all the contact from the orgnizer in
* an array sorted by last name.
*
* #return An array of Person objects.
*
*/
public Person[] getSortedListByLastName()
{
Map<String, Person> sorted = new TreeMap<String, Person>(staff);
ArrayList<Person> sortedArrayList = new ArrayList<Person>();
for (Person s: sorted.values()) {
sortedArrayList.add(s);
}
Person sortedArray[] = new Person[sortedArrayList.size()];
sortedArrayList.toArray(sortedArray);
return sortedArray;
}
private Map<String, Person> staff = new HashMap<String, Person>();
public static void main(String[] args)
{
OrganizeThis testObj = new OrganizeThis();
Person person1 = new Person("J", "W", "111-222-3333", "JW#ucsd.edu");
Person person2 = new Person("K", "W", "345-678-9999", "KW#ucsd.edu");
Person person3 = new Person("Phoebe", "Wang", "322-111-3333", "phoebe#ucsd.edu");
Person person4 = new Person("Nermal", "Johnson", "322-342-5555", "nermal#ucsd.edu");
Person person5 = new Person("Apple", "Banana", "123-456-1111", "apple#ucsd.edu");
testObj.add(person1);
testObj.add(person2);
testObj.add(person3);
testObj.add(person4);
testObj.add(person5);
System.out.println(testObj.findByEmail("JW#ucsd.edu"));
System.out.println("------------" + '\n');
Person a[] = testObj.find("W");
for (Person p : a)
System.out.println(p);
System.out.println("------------" + '\n');
a = testObj.find("W");
for (Person p : a)
System.out.println(p);
System.out.println("SORTED" + '\n');
a = testObj.getSortedListByLastName();
for (Person b : a) {
System.out.println(b);
}
}
}
Person class:
public class Person implements Comparable
{
String firstName;
String lastName;
String telephone;
String email;
public Person()
{
firstName = "";
lastName = "";
telephone = "";
email = "";
}
public Person(String firstName)
{
this.firstName = firstName;
}
public Person(String firstName, String lastName, String telephone, String email)
{
this.firstName = firstName;
this.lastName = lastName;
this.telephone = telephone;
this.email = email;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public String getTelephone()
{
return telephone;
}
public void setTelephone(String telephone)
{
this.telephone = telephone;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public int compareTo(Object o)
{
String s1 = this.lastName + this.firstName;
String s2 = ((Person) o).lastName + ((Person) o).firstName;
return s1.compareTo(s2);
}
public boolean equals(Object otherObject)
{
// a quick test to see if the objects are identical
if (this == otherObject) {
return true;
}
// must return false if the explicit parameter is null
if (otherObject == null) {
return false;
}
if (!(otherObject instanceof Person)) {
return false;
}
Person other = (Person) otherObject;
return firstName.equals(other.firstName) && lastName.equals(other.lastName) &&
telephone.equals(other.telephone) && email.equals(other.email);
}
public int hashCode()
{
return this.email.toLowerCase().hashCode();
}
public String toString()
{
return getClass().getName() + "[firstName = " + firstName + '\n'
+ "lastName = " + lastName + '\n'
+ "telephone = " + telephone + '\n'
+ "email = " + email + "]";
}
}
You get the wrong idea, actually.
Here's the gist:
Map<K,V> is a mapping from K key to V value
TreeMap<K,V> is a SortedMap<K,V> that sorts the keys, not the values
So, a TreeMap<String,Person> would sort based on e-mail addresses, not the Person's first/last names.
If you need a SortedSet<Person>, or a sorted List<Person> then that's a different concept, and yes, Person implements Comparable<Person>, or a Comparator<Person> would come in handy.
API links
java.lang.Comparable<T> - defines the "natural ordering" of objects of a type
java.util.Comparator<T> - defines a "custom" comparison of objects of a type
java.util.Map<K,V> - maps keys to values, not the other way around
java.util.SortedMap<K,V> - sorts the keys, not the values
java.util.SortedSet<E> - a set that is ordered
java.util.Collections.sort(List) - a utility method to sort
Also has an overload that takes a Comparator
Related questions
When to use Comparable vs Comparator
Sorting a collection of objects
Sorting an ArrayList of Contacts
Java: SortedMap, TreeMap, Comparable? How to use?
Example
There are plenty of examples out there already, but here's one more:
import java.util.*;
public class Example {
static String lastName(String fullName) {
return fullName.substring(fullName.indexOf(' ') + 1);
}
public static void main(String[] args) {
Map<String,String> map = new TreeMap<String,String>();
map.put("001", "John Doe");
map.put("666", "Anti Christ");
map.put("007", "James Bond");
System.out.println(map);
// "{001=John Doe, 007=James Bond, 666=Anti Christ}"
// Entries are sorted by keys!
// Now let's make a last name Comparator...
Comparator<String> lastNameComparator = new Comparator<String>() {
#Override public int compare(String fullName1, String fullName2) {
return lastName(fullName1).compareTo(lastName(fullName2));
}
};
// Now let's put all names in a SortedSet...
SortedSet<String> namesByLastName =
new TreeSet<String>(lastNameComparator);
namesByLastName.addAll(map.values());
System.out.println(namesByLastName);
// "[James Bond, Anti Christ, John Doe]"
// Names sorted by last names!
// Now let's use a List instead...
List<String> namesList = new ArrayList<String>();
namesList.addAll(map.values());
System.out.println(namesList);
// "[John Doe, James Bond, Anti Christ]"
// These aren't sorted yet...
Collections.sort(namesList);
System.out.println(namesList);
// "[Anti Christ, James Bond, John Doe]"
// Sorted by natural ordering!
// Now let's sort by string lengths...
Collections.sort(namesList, new Comparator<String>() {
#Override public int compare(String s1, String s2) {
return Integer.valueOf(s1.length()).compareTo(s2.length());
}
});
System.out.println(namesList);
// "[John Doe, James Bond, Anti Christ]"
// SUCCESS!!!
}
}
As polygenelubricants nicely explained, a SortedMap is sorted by the key, not by the value.
However, it's with help of LinkedHashMap possible to reorder a Map the way you want. A LinkedHashMap maintains insertion order like a List does.
First step is to get hold of the key/value pairs in a sortable data structure, e.g. List<Entry<K, V>> which you in turn sort using Collections#sort() with help of a Compatator<Entry<K, V>> and finally repopulate a LinkedHashMap with it (not a HashMap or you will lose the ordering again).
Here's a basic example (leaving obvious runtime exception handling aside):
// Prepare.
Map<String, String> map = new HashMap<String, String>();
map.put("foo", "bar");
map.put("bar", "waa");
map.put("waa", "foo");
System.out.println(map); // My JVM shows {waa=foo, foo=bar, bar=waa}
// Get entries and sort them.
List<Entry<String, String>> entries = new ArrayList<Entry<String, String>>(map.entrySet());
Collections.sort(entries, new Comparator<Entry<String, String>>() {
public int compare(Entry<String, String> e1, Entry<String, String> e2) {
return e1.getValue().compareTo(e2.getValue());
}
});
// Put entries back in an ordered map.
Map<String, String> orderedMap = new LinkedHashMap<String, String>();
for (Entry<String, String> entry : entries) {
orderedMap.put(entry.getKey(), entry.getValue());
}
System.out.println(orderedMap); // {foo=bar, waa=foo, bar=waa}
Needless to say that this is after all not the right datastructure for your purpose ;)

Categories

Resources