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;
}
}
}
Related
I'm trying to create a contact service to add/modify/delete contacts and I'm stuck on how to modify a contact. This is the Contact class that I'm starting with:
public class Contact {
private String contactId;
private String firstName;
private String phone;
public Contact (String contactId, String firstName, String phone) {
if (contactId == null || contactId.length() > 10) {
throw new IllegalArgumentException("invalid contact ID");
}
if (firstName == null || firstName.length() > 10) {
throw new IllegalArgumentException("invalid first name");
}
if (phone == null || phone.length() != 10) {
throw new IllegalArgumentException("invalid phone number");
}
this.contactId = contactId;
this.firstName = firstName;
this.phone = phone;
}
public String getContactId() {
return contactId;
}
public String getFirstName() {
return firstName;
}
public String getPhone() {
return phone;
}
}
In my contact service class, I have written the methods for creating and getting a contact
public class ContactService {
int Id = 000;
//create contactList array
private ArrayList<Contact> contactList = new ArrayList<>();
public Contact getContact(String ID) {
//iterates through contact list
Iterator<Contact> itr = contactList.iterator();
while (itr.hasNext()) {
Contact contact = itr.next();
if (contact.getContactId().equals(ID)) {
//returns contact object for matching ID
return contact;
}
}
return null;
}
//adds a contact to the contactList array
public Contact addContact(String firstName, String Phone) {
Id++; //increment ID to make a unique ID
//convert integer to string for ArrayList
String contactId = Integer.toString(Id);
//create contact object
Contact contact = new Contact(contactId, firstName, Phone);
//add contact to list
contactList.add(contact);
return contact;
}
For my updateContact() method, I want to call getContact() which should return a contact object but I'm not sure how to update the object from there. This is probably this closest I've come:
public void updateContact(String contactId, String firstName, String phone) {
getContact(contactId);
contact.setFirstName(firstName);
contact.setPhone(phone);
}
However, not only does this approach not work but it would require me to create setter methods. If I do that the new input does not go through the null and length checks contained in the Contact constructor.
Your update method does not work because you are calling getContact without storing return value of the method
public void updateContact(String contactId, String firstName, String phone) {
Contact contact = getContact(contactId);
contact.setFirstName(firstName);
contact.setPhone(phone);
}
For make sure your checks are called you can call setter methods in your constructor like this
public Contact(String contactId, String firstName, String phone) {
setFirstName(firstName);
setPhone(phone);
setContactId(contactId);
}
And check your values into setter methods
public void setFirstName(String firstName) {
if (firstName == null || firstName.length() > 10) {
throw new IllegalArgumentException("invalid first name");
}
this.firstName = firstName;
}
public void setPhone(String phone) {
if (phone == null || phone.length() != 10) {
throw new IllegalArgumentException("invalid phone number");
}
this.phone = phone;
}
public void setContactId(String contactId) {
if (contactId == null || contactId.length() > 10) {
throw new IllegalArgumentException("invalid contact ID");
}
this.contactId = contactId;
}
Furthermore, you can improve your performance using an HashMap instead of ArrayList, so that you don't need to iterate whole the list every time you need a contact.
private final Map<String, Contact> contactList = new HashMap<>();
public Contact getContact(String ID) {
return contactList.get(ID);
}
//adds a contact to the contactList array
public Contact addContact(String firstName, String Phone) {
Id++;
String contactId = Integer.toString(Id);
Contact contact = new Contact(contactId, firstName, Phone);
return contactList.put(contactId, contact);
}
As #Atryom noted, you can add the same null and length checks that you have in your constructor to your setter methods. There's no restriction specifying that your setters cannot have those checks.
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));
}
I got a lab assignment dealing with arrays, sorting them and adding comparable interface to my two classes. I have to modify a Customer class so it implements a comparable interface. Then I have to sort an array of objects created by this class.
These are the steps outlined by my worksheet:
open the customer and SortedCustomersApp java files (see below):
public class Customer
{
private String email;
private String firstName;
private String lastName;
public Customer(String email, String firstName, String lastName)
{
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
}
public void setEmail(String email)
{
this.email = email;
}
public String getEmail()
{
return email;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getFirstName()
{
return firstName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public String getLastName()
{
return lastName;
}
}
This is the sorted customers app:
import java.util.Arrays;
public class SortedCustomersApp
{
public static void main(String[] args)
{
}
}
add code to the customer class to implement the comparable interface. The compareTo method you create should compare the email field of the current customer with the email field of another customer. To do that you cant use the < or > operators because the email field is a string. Instead, use the compareToIgnoreCase method of the string class. This method compares the string it's executed on with the string that's passed to it as an argument. If the first string is less than the 2nd string, this methods returns a negative integer. if the first string is greater than the second string, it returns a positive integer. And if the 2 strings are equal, it returns 0.
3.Add code to the SortedCustomersApp class that creates an array of Customer objects that can hold 3 elements, and create and assign Customer objects to those elements. Be sure that the email values you assign to the objects aren't in alphabetical order. Sort the array.
code a "for each" loop that prints the email, firstName, and lastName fields of each Customer object on a separate line.
compile and test the program
This program needs to have user input for email, firstName, and lastName but when I tried adding user input to the customer app I got errors saying I cant convert a string to scanner type, but the user inputs need to be a string so that's also a problem.
you implement Comaprable in order to define for Collections.sort() what criteria you use compare instances.For example if I was to compare students by their grades:
public class Student implements Comparable<Student>{
int age;
int grade;
//this method is used in Collections.sort() to determine what is bigger
//for me its grades that matter so that's what I do in code
#override
compareTo(Student other){
if(this.grade>other.grade){
return 1;
}else if(grade<other.grade){
return -1;
}
return 0;
}
}
then you just create an ArrayList students for example and call
Collections.sort(students)
EDIT: to answer you question in comments. If you use a Collection like ArrayList you would call Collections.sort() , if you use a regular array, then Arrays.sort(). In both cases the Objects to be sorted need to implement Comparable just like I showed you.
You can compare two String's lexicographically calling compareTo method on String object, code looks like this
public class Customer implements Comparable<Customer>{
private String email;
public Customer(String email) {
this.email = email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
#Override
public int compareTo(Customer otherCustomer) {
return this.email.compareToIgnoreCase(otherCustomer.email);
}
#Override
public String toString() {
return "Customer{" +
"email='" + email + '\'' +
'}';
}
}
You can sort list of objects using Collections.sort method
List<Customer> list = new ArrayList<>();
list.add(new Customer("abc#bc.com"));
list.add(new Customer("des#bc.com"));
list.add(new Customer("bbc#bc.com"));
list.add(new Customer("aec#bc.com"));
Collections.sort(list);
modified customer class
public class Customer implements Comparable<Customer>{
:
public int compareTo(Object obj)
{
Customer cus = (Customer) obj;
// write your comparison code here
// return 1, 0, or -1 on the basis of your requirement
}
:
}
Then perform
Collection.sort(customerList);
in main method to sort the data.
There is two ways to take String as an input from user:
1) Buffered Reader
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String email = reader.readLine(); // email, firstname etc.
2) Scanner Class
Scanner input = new Scanner(System.in);
String email = input.next();
Now create an Customer object and add setter method to set the value.
Good day!
I created overloading constructors as follows:
public ContactsBean(String firstName, String lastName,
String telNumber, String email) {
this.id = count;
count = count + 1;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.telNumber = telNumber;
}
public ContactsBean() {
this.id = count;
count = count + 1;
}
I want to auto-increment the id so i used this variables:
private static int count;
private int id;
My problem is, when I instantiate the ContactsBean() contacts = new ContactsBean(), the value of id is incremented by 2..
2,4,6,8... etc.
Why? How can I do the auto number of ID increment by 1?
Thank you.
EDIT:
Action:
private ContactsBean contacts = new ContactsBean();
private ContactsManager contactsManager = new ContactsManager();
public String add() {
contactsManager.addContacts(contacts);
return SUCCESS;
}
Manager:
private ContactsDAO contactsDAO = ContactsDAO.getInstance();
private List<ContactsBean> contactsList = contactsDAO.getContactsList();
public void addContacts(ContactsBean contact) {
contactsList.add(contact);
}
First, DRY (do not repeat yourself), would be better:
public ContactsBean(String firstName, String lastName,
String telNumber, String email){
this();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.telNumber = telNumber;
}
Second, there is no increment by two in your code. Please paste in your test code.
Try removing the first set of parentheses from
ContactsBean() contacts = new ContactsBean();.
That is, try this constructor:
ContactsBean contacts = new ContactsBean();
I think you are creating two objects of ContactsBean in your other classes may be you are unaware of it. You have to check the code.
Have you tried debugging your code setting a breakpoint on both constructors?
The suggestion of Eng. Fouad is a good tip but it's not gonna solve your problem.
Also note that your counter is not thread-safe (the problem has nothing to do with it, though. In that case your counter would have a lower value than it should)
And if you really need to keep track on how many objects you actually create, I don't think the best way to do this is with a static attribute in a Java Bean...
It might be due to the copy constructor calling the no-arg version of your contstructor.
(Or am I suffering from C++ sickness?)
My suggestion would be to not try to increment the contactID in the contructor, but either get it from the newly created database object where the ID is being incremented by databse via identity specification, or since you are getting the list of contacts base your next id off of contactsDAO.getContactsList().size()+1.
I'd also recommend changing from:
private ContactsDAO contactsDAO = ContactsDAO.getInstance();
private List<ContactsBean> contactsList = contactsDAO.getContactsList();
public void addContacts(ContactsBean contact) {
contactsList.add(contact);
}
To something like:
private ContactsDAO contactsDAO = ContactsDAO.getInstance();
private List<ContactsBean> contactsList;
public void addContacts(ContactsBean contact) {
int id = getContactList().size()+1;
contact.setId(id);
contactsList.add(contact);
}
public List<ContactsBean> getContactList(){
return contactsDAO.getContactsList();
}
alternatively if you are able to remove contacts from the database, this number might not be accurate for the ID. You could create a query based on such as:
select MAX(ID) from contacts
This will return the largest id number used.
private static int count = 0;
private int id;
// ...
public ContactsBean(String firstName, String lastName,String telNumber, String email)
{
this();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.telNumber = telNumber;
}
public ContactsBean()
{
id = ++count;
}
I learned that Struts2 generates the instance of an object(beans) automatically in the Action Classes so no need to instantiate it....
My code before is
private ContactsBean contacts = new ContactsBean();
private ContactsManager contactsManager = new ContactsManager();
public String add() {
contactsManager.addContacts(contacts);
return SUCCESS;
}
//getters and setters
I changed it to..
private ContactsBean contacts;
private ContactsManager contactsManager = new ContactsManager();
public String add() {
contactsManager.addContacts(contacts);
return SUCCESS;
}
//getters and setters
And it works...
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 ;)