How to get more search data using binary search? - java

First of all I apologize for my english and this is my first time asking on stackoverflow so if i miss something please point it out.
So I'm new to java and trying out binary search with the help from my friend. The code is to display product information once searched with product ID. I manage to make it return the index number where Id is found but the problem is when i put in multiple same ID it only show 1 data. I want my program to show all the index where the ID-12 is found.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MyListBinarySearch {
public static void main(String a[]){
List<Emp> empList = new ArrayList<Emp>();
empList.add(new Emp(12,"Apple,50,10-5-2014"));
empList.add(new Emp(12,"Apple,50,5-5-2014"));
empList.add(new Emp(124,"Apple,50,2-5-2014"));
empList.add(new Emp(302,"Apple,50,2-5-2014"));
empList.add(new Emp(12,"Apple,50,2-5-2014"));
Emp searchKey = new Emp(12,"String");
int index = Collections.binarySearch(empList, searchKey, new EmpComp());
System.out.println("Index of the searched key: "+index);
}
}
class EmpComp implements Comparator<Emp>{
public int compare(Emp e1, Emp e2) {
if(e1.getEmpId() == e2.getEmpId()){
return 0;
} else {
return -1;
}
}
}
class Emp {
private int empId;
private String empInfo;
public Emp(int id, String info){
this.empId = id;
this.empInfo = info;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpInfo() {
return empInfo;
}
public void setEmpInfo(String empInfo) {
this.empInfo = empInfo;
}
#Override
public String toString(){
return empId+" : "+empInfo;
}
}
The out is "Index of searched key: 2"
I want to display all the index where the search key is found.
How do i do that ? Do i need to loop ?

You have two problems:
Your comparator should return something greater than 0 when the current element is greater than the compared element, 0 when the elements are equals and less than 0 when the current element is less than the compared element. Your current implementation doesn't cover this.
Binary search works on sorted arrays/lists only. Your list is not sorted by id.
After fixing this issues, then you will effectively use binary search. After retrieving the index where the element is 12, you can search around the element to retrieve all the elements that have the same Id.
This is an idea how to implement it:
int index = Collections.binarySearch(empList, searchKey, new EmpComp());
List<Emp> empsWithId12 = new ArrayList<Emp>();
for (int i = index - 1; i >= 0; i--) {
Emp emp = empList.get(i);
if (emp.getId() == 12) {
empsWithId12.add(emp);
} else {
break;
}
}
Collections.reverse(empsWithId12);
for (int i = index; i < empList.size(); i++) {
Emp emp = empList.get(i);
if (emp.getId() == 12) {
empsWithId12.add(emp);
} else {
break;
}
}
Note that the idea above can be greatly improved by moving the logic into a method.

Once you get the index of the the search key, you can go backwards or left of the list to find all other indexes with key equal to the search key

Related

How to get rid of NullPointerException when removing elements from array? [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed last month.
Given the following task. We have an Employee and a Company classes. Each instance of Employee class is stored in array Employee[] employees in the Company class. I need a method which removes an instance of Employee in the array Employee[] employees by id.
I managed to write the following code:
public class Employee {
protected final int id;
protected String name;
public Employee(int id, String name) {
this.id = id;
this.name= name;
}
public int getId() {
return id;
}
}
public class Company {
private Employee[] employees;
private int size;
private static final int defaultCapacity = 5;
public Company() {
this(defaultCapacity);
}
public Company(int capacity) {
if (capacity <= 0)
throw new RuntimeException("capacity is required");
employees = new Employee[capacity];
}
public Employee removeEmployee(int id) {
Collection<Employee> employeeList = Arrays.asList(employees)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
Employee[] employeeArray = employeeList.toArray(Employee[]::new);
for (int i = 0; i < size; i++) {
if(employeeArray[i].getId() == id) {
Employee removedEmployee = employees[i];
employeeList.remove(employeeArray[i]);
employees = employeeList
.stream()
.filter(Objects::nonNull)
.toArray(Employee[]::new);
return removedEmployee;
}
}
return null;
}
}
The problem is that my method public Employee removeEmployee(int id) throws NullPointerException if an element for removal is not found.
Question:
How can I rewrite the method public Employee removeEmployee(int id) using, for instance, Streams API and Optional in oder to get rid of NullPointerException in the method public Employee removeEmployee(int id)?
N.B.: The length of the array Employee[] employees declared in the class Company must be reduced after the element has been successfully removed.
There is a lot of ways to get rid of the NullPointerException here.
If you want to keep using the stream API, you may want to use filter and findAny.
For example, you could modify the method to the following:
public Employee removeEmployee(int id) {
Optional<Employee> employee = Arrays.stream(employees)
.filter(Objects::nonNull)
.filter(x -> x.getId() == id).
.findAny();
if(employee.isEmpty())
return null;
employees = Arrays.stream(employees).filter(x -> x != employee.get()).toArray(Employee[]::new);
return employee.get();
}
However, I would highly advise using a List or even a Map instead of an Array for employees as this makes things way easier and faster:
public Employee removeEmployee(int id){
Optional<Employee> toRemove = employees.stream().filter(x -> x.getId() == id).findAny();
if(toRemove.isEmpty())
return null;
employees.remove(toRemove.get());
return toRemove.get();
}
Or not to use the Stream API:
public Employee removeEmployee(int id){
int idx;
for(idx = 0; idx < employees.length; idx++){
if(employees[idx] != null && employees[idx].getId() == id)
break;
}
if(idx == employees.length)
return null;
Employee value = employees[idx];
Employee[] newArr = new Employee[employees.length - 1];
// the parameters here are left as an exercise to the reader :P
System.arraycopy(newArr, ...);
System.arraycopy(newArr, ...);
employees = newArr;
return value;
}
The length of the array Employee[] employees declared in the class Company must be reduced after the element has been successfully removed.
Streams doesn't buy you a lot in this case.
What you're supposed to do is to find the element with the target id, and if such an element exists, allocate a new array in memory with a length smaller by 1 copy all the elements apart from the one that was found, and assign employees with the reference to the new array.
To reduce the length, we can make use of the System.arraycopy(). First copy the elements before the target, and then after the target.
That's how it would look like with a plain index-based for-loop.
public Employee removeEmployee(int id) {
Employee result = null;
int index = -1;
for (int i = 0; i < employees.length; i++) {
if (employees[i] != null && employees[i].getId() == id) {
result = employees[i];
employees[i] = null;
break;
}
}
if (result != null) {
reduceLength(index);
}
return result;
}
public void reduceLength(int i) {
Employee[] newEmployees = new Employee[employees.length - 1];
System.arraycopy(employees, 0, newEmployees, 0, i);
System.arraycopy(employees, i + 1, newEmployees, i, employees.length - (i + 1));
employees = newEmployees;
}
If you want to do weird stuff and use Stream API and Optional at all costs, here how it can be done (but I would recommend to stick with the code above):
public Optional<Employee> removeEmployee(int id) {
Optional<Integer> index = IntStream.range(0, employees.length)
.filter(i -> employees[i] != null)
.filter(i -> employees[i].getId() == id)
.boxed() // otherwise will get OptionalInt which lacks map() method
.findFirst();
Optional<Employee> result = index.map(i -> employees[i]);
index.ifPresent(this::reduceLength);
return result;
}
Considering it's homework and constraints mentioned, i believe you are supposed to do all the work using the array only.
I'll provide some guideline and leave the actual implementation to you:
public class Company {
private Employee[] employees;
private int size;
public Employee removeEmployee(int id) {
int index = -1;
//find the index of employee with required id, you have mostly done that
if (index == -1) {
return null;
}
//save found employee to variable
//remove from array
//shift array to the left
//do not forget to use and reassign size variable where appropriate
}
//some extra
public void addEmployee(Employee employee) {
//resize array if necessary
//add employee at correct position in array
//do not forget to use and reassign size variable where appropriate
}
}
If you get stuck, you can look at the ArrayList class, your task is basically a simplified version of it. I strongly advise you to use this as source of inspiration only and not to copy the source code!!!

Phonebook Alphabetical Sorting according to the field First Name

I am trying to get my JavaFx application of a phone book in order to sort alphabetically the contacts list according to the first name. However, the existing loop is not listing the names in the correct order. Here is the sorting method:
public static void sortContactList() {
try {
for (int i = 0; i < contactList.length - 1; i++) {
for (int j = i + 1; i < contactList.length; j++) {
if ((contactList[j].first.toCharArray()[0]) < (contactList[i].first.toCharArray()[0])) {
Entry tmp = contactList[j];
contactList[j] = contactList[i];
contactList[i] = tmp;
}
}
}
} catch (NullPointerException exc) {}
}
Below it is shown how the contact list is made up. It is stored in a file with first being the person's first names as strings:
class Entry {
public String first, last, number, note;
}
public class Phonebookfor1510 {
public static Entry[] contactList;
public static int num_entries;
public static void main(String args[]) throws Exception {
int i;
char C;
String code, Command;
contactList = new Entry[200];
num_entries = 0;
The rest of my code works with it. I am just wondering why it is not sorting the list of values alphabetically. Any help would be greatly appreciated!!
I think the second for loop will never end without a NullPointerException. You are increasing j, but checking for i<contactList.
You could use Arrays.sort() in this case.
The Best thing You can Do is Use TreeSet. Like the given example
TreeSet<String> stringSet = new TreeSet<>();
stringSet.add("Mummy");
stringSet.add("Mahima");
stringSet.add("Gaurav");
stringSet.add("Naresh");
stringSet.add("Bhawna");
for(String s : stringSet) {
System.out.println(s);
}
Output :
Bhawna
Gaurav
Mahima
Mummy
Naresh
take a look at this sample code:
public class Entry implements Comparable<Entry>{
private Long id;
private String fullName;
private String phoneNumber;
public int compareTo(Entry other) {
if(other==null) throw new NullPointerException();
/*
-1 : phonenumber of current entry is smaller that other
0 : phonenumber of current entry is equal to other
+1 : phonenumber of current entry is greater than other
*/
return (this.getFullName().compareTo(other.getPhoneNumber()));
}
public static void main(String[] args){
Entry[] entriesArray = getEntriesArrayListFromSomewhere();
Arrays.sort(entriesArray);
}
//getters ans setters
}

Removing Duplicate Entries in Array - Java

For Java practice, I am trying to create a method inside my EmployeesDirectory Class that:
Removes Duplicate entries from the array
The array should be the same length after removing duplicates
Non-Empty entries should be making a contiguous sequence at the beginning of the array - and the actualNum should keep a record of the entries
Duplicate Means: Same Name, Position and Salary
Here is my Current Code:
I am unsure on how to implement this - any help would be appreciated
class EmployeeDirectory {
private Employee dir[];
private int size;
private int actualNum;
public EmployeeDirectory(int n) {
this.size = n;
dir = new Employee[size];
}
public boolean add(String name, String position, double salary) {
if (dir[size-1] != null) {
dir[actualNum] = new Employee(name, position, salary);
actualNum++;
return true;
} else {
return false;
}
}
}
I'd rather you did not write a distinct method for removing duplicates. If I were you, I would search for duplicates in add method and then instantly decide whether I need to add Employee.
Also, why don't you use Sets (link for HashSet) instead of arrays for your purpose? Sets by their own definition disallow adding duplicates, so they seem to be appropriate as a solution
First of all, Override equals and hashCode methods in Employee class as follow
#Override
public boolean equals(Object other) {
if(this == other) return true;
if(other == null || (this.getClass() != other.getClass())){
return false;
}
Employee guest = (Employee) other;
return Objects.equals(guest.name, name)
&& Objects.equals(guest.position, position)
&& Objects.equals(guest.salary, salary);
}
#Override
public int hashCode() {
return Arrays.hashCode(new Object[] {
name,
position,
salary
});
}
Then you can use Stream API distinct method to remove duplicates
Returns a stream consisting of the distinct elements (according to
Object.equals(Object)) of this stream.
You can do it like so
Employee e1 = new Employee("John", "developer", 2000);
Employee e2 = new Employee("John", "developer", 2000);
Employee e3 = new Employee("Fres", "designer", 1500);
Employee[] allEmployees = new Employee[100];
allEmployees[0] = e1;
allEmployees[1] = e2;
allEmployees[2] = e3;
allEmployees = Arrays.asList(allEmployees).stream().distinct()
.toArray(Employee[]::new);
Arrays.asList(allEmployees).forEach(System.out::println);
Output: (keeping both empty and non-empty entries)
John developer 2000.0
Fres designer 1500.0
null
Unfortunately, I have not got the Employee class to verify my code, but try this:
void removeDuplicates() {
int length = dir.length;
HashSet set = new HashSet(Arrays.asList(dir));
dir = new Employee[length];
Employee[] temp = (Employee[]) set.toArray();
for (int index = 0; index < temp.length; index++)
dir[index] = temp[index];
}
The code must remain the size of array after deletion the duplicates. At the beginning of array there must be valid Employees, at the end - nulls.
And don't forget to add this at the beginning of your .java file
import java.util.Arrays;
import java.util.HashSet;
If your task states as "remove duplicates from array" (i. e. you cannot use ArrayList or control when adding items), you can use the following approach:
public void removeDuplicates() {
Set<Employee> d = new HashSet<>(); // here to store distinct items
int shift = 0;
for (int i = 0; i > dir.length; i++) {
if (d.contains(dir[i])) { // duplicate, shift += 1
shift++;
} else { // distinct
d.add(dir[i]); // copy to `d` set
dir[i - shift] = dir[i]; // move item left
}
}
for (int i = d.size(); i < dir.length; i++)
dir[i] = null; // fill rest of array with nulls
actualNum = d.size();
}
Here, shift variable stores number of duplicates found in the array so far. Every distinct item is moved to shift positions left in order to make sequence continuous while keeping initial ordering. Then remaining items are altered to nulls.
To make hash-based collections work with Employee instances correctly, you also need to override hashCode() and equals() methods as follows:
public class Employee {
//...
#Override
public int hashCode() {
return Objects.hash(name, position, salary);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (!o.getType().equals(this.getType()) return false;
Employee e = (Employee) o;
return Objects.equals(e.name, name)
&& Objects.equals(e.position, position)
&& Objects.equals(e.salary, salary); // or e.salary == salary, if it primitive type
}
}

ArrayList sort for String, int [duplicate]

This question already has answers here:
Sort ArrayList of custom Objects by property
(29 answers)
How to sort Arraylist of objects
(3 answers)
Closed 9 years ago.
I m looking to sort an ArrayList which is of the type <String,int>, according to int.
So, my variable is var<String,int>
India 2
Pakistan 3
USA 1
The output becomes:
USA 1
India 2
Pakistan 3
I am confused how does it works with int. Collections.sort(var) does not works with it.
You can't use ArrayList of type
<String, int>
You can't have primitives in ArrayList as ArrayList holds objects. So, the closest you can do is to store Integer objects.
ArrayList can be of only one type if you are parameterizing it.
If you want to hold String and int, you can create a class CountryInfo with fields name and rank. Then create
ArrayList<CountryInfo> list =new ArrayList<CountryInfo>();
Then you can use
Collections.sort(list, <Comparator>)
I have created an example where you can sort your ArrayList even if its with objects. You can read through it an see if it's helps.
I have made two classes and a test class:
First class is Country:
public class Country {
private String countryName;
private int number;
public Country(String countryName, int number){
this.countryName = countryName;
this.number = number;
}
public String getCountryName(){
return countryName;
}
public void setCountryName(String newCountryName){
countryName = newCountryName;
}
public int getNumber(){
return number;
}
public void setNumber(int newNumber){
number = newNumber;
}
public String toString(){
return getCountryName() + getNumber();
}
}
Next class is Methods:
public class Methods {
private Country country;
private ArrayList<Country> overview = new ArrayList<Country>();
private ArrayList<Country> overviewSorted = new ArrayList<Country>();
int [] test;
public void regCountry(String countryname, int numbers){
if(!(countryname == "" && numbers == 0)){
overview.add(new Country(countryname, numbers));
} else {
System.out.println("The input was null");
}
}
public void showRegisteredCountries(){
if(!(overview.size() < 0)){
for(int i = 0; i < overview.size(); i++){
System.out.println("The country: " + overview.get(i).getCountryName() + " has the number: " + overview.get(i).getNumber() + " registered");
}
} else {
System.out.println("There are no country registered");
}
}
public void numbersOverFromArrayList(){
if(!(overview.size() < 0)){
test = new int [overview.size()];
for(int i = 0; i < overview.size(); i++){
test[i] = overview.get(i).getNumber();
}
}
}
public void sortArrayAndCopyItBack(){
if(!(test.length < 0)){
java.util.Arrays.sort(test);
for(int i = 0; i < test.length; i ++){
for(int j = 0; j < overview.size(); j++){
if(test[i] == overview.get(j).getNumber()){
overviewSorted.add(new Country(overview.get(j).getCountryName(), overview.get(j).getNumber()));
}
}
}
}
}
public void showTableSorted(){
if(!(overviewSorted.size() < 0)){
for(int i = 0; i < overviewSorted.size(); i++){
System.out.println("Country name: " + overviewSorted.get(i).getCountryName() + " with number: " + overviewSorted.get(i).getNumber());
}
} else {
System.out.println("There are non countrys in table that is sorted");
}
}
}
Next is the test class:
public class test2 {
public static void main(String [] args){
Methods methodes = new Methods();
for(int i = 0; i < 4; i++){
String inCountry = JOptionPane.showInputDialog("Country:");
String inNumber = JOptionPane.showInputDialog("number:");
String country = inCountry;
int number = Integer.parseInt(inNumber);
methodes.regCountry(country, number);
}
methodes.showRegisteredCountries();
methodes.numbersOverFromArrayList();
methodes.sortArrayAndCopyItBack();
methodes.showTableSorted();
}
}
My output:
The country: Norway has the number: 5 registered
The country: Sweden has the number: 2 registered
The country: Denmark has the number: 9 registered
The country: Finland has the number: 7 registered
Country name: Sweden with number: 2
Country name: Norway with number: 5
Country name: Finland with number: 7
Country name: Denmark with number: 9
That is not an ArrayList. Use TreeMap in Stead.
Map<String, Integer> countryInfo = new TreeMap<String,Integer>();
This way it will be sorted automatically
You can sort
use Collections.sort(list,Comparator implementation)
in the implementation(here I have used anonymous implementation) override compare method
where you
get last character of each string convert to string and compare them
ArrayList<String> a=new ArrayList<String>();
a.add("India 2");
a.add("Pakistan 3");
a.add("USA 1");
Collections.sort(a, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
Integer i=Integer.valueOf(o1.substring((o1.length() -1),o1.length()));
Integer j=Integer.valueOf(o2.substring((o2.length() -1),o2.length()));
return i.compareTo(j);
}
});
You can optimist code
ArrayList is a collection of one type of object. It is not like maps that can take two inputs.
Therefore, there are three options:
1. Make use of a TreeMap that contains both a Key and a Map and is automatically sorted by key or
2. Make use of an unsorted map and sort with a comparator - see Sort a Map<Key, Value> by values (Java) or
3. Use an arraylist of a custom class with a comparator.
-
1) Using a TreeMap
Treemaps are an implementation of red-black trees. See: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/TreeMap.html
TreeMap<Integer,String> countries = new TreeMap<Integer,String>();
countries.put(2, "India");
countries.put(1, "USA");
countries.put(3, "Pakistan");
Iterator<Entry<Integer, String>> it = countries.entrySet().iterator();
Entry<Integer, String> entry;
while(it.hasNext())
{
entry = it.next();
System.out.println(entry.getValue() + " " + entry.getKey());
}
And this Produces:
USA 1
India 2
Pakistan 3
-
2) Make use of an unsorted map and sort with a comparator
See: Sort a Map<Key, Value> by values (Java) as the answer is very will written.
-
3) Using an ArrayList with Country Class
In order to support your example you would need to create a Country class.
You would need to do the following:
Implement Comparable within your country class and place the logic for the comparison within there.
Create a custom comparator that you will give to your Collection.sort invocation.
import java.util.ArrayList;
import java.util.Collections;
import java.util.InputMismatchException;
import java.util.Iterator;
public class CountrySortExample {
public static void main(String[] args) {
new CountrySortExample();
}
public ArrayList<Country> countries = new ArrayList<Country>();
public CountrySortExample()
{
countries.add(new Country("India",2));
countries.add(new Country("Pakistan",3));
countries.add(new Country("USA",1));
Collections.sort(countries);
Iterator<Country> it = countries.iterator();
Country count;
while(it.hasNext())
{
count = it.next();
System.out.println(count.CountryName + " " + count.CountryIndex);
}
}
class Country implements Comparable
{
public String CountryName;
public int CountryIndex;
public Country(String CountryName,int CountryIndex )
{
this.CountryName = CountryName;
this.CountryIndex = CountryIndex;
}
#Override
public int compareTo(Object o) {
if(! (o instanceof Country))
throw new InputMismatchException("Country is expected");
Country other = (Country)o;
if(other.CountryIndex > CountryIndex)
return -1;
else if(other.CountryIndex == CountryIndex)
return 0;
else return 1;
}
}
}
Further information is available at: http://www.mkyong.com/java/java-object-sorting-example-comparable-and-comparator/
If you have an object that you want to sort in more than one way, you define a Comparator class for each type of sort you want to do.
Using the example that the OP gave, here's one way to define the object and Comparators.
Here's one test result:
CountryRating [name=India, rating=2]
CountryRating [name=Pakistan, rating=3]
CountryRating [name=USA, rating=1]
CountryRating [name=USA, rating=1]
CountryRating [name=India, rating=2]
CountryRating [name=Pakistan, rating=3]
And here's the example code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CountryRating {
private String name;
private int rating;
public CountryRating(String name, int rating) {
this.name = name;
this.rating = rating;
}
public String getName() {
return name;
}
public int getRating() {
return rating;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("CountryRating [name=");
builder.append(name);
builder.append(", rating=");
builder.append(rating);
builder.append("]");
return builder.toString();
}
public static void main(String[] args) {
List<CountryRating> list = new ArrayList<CountryRating>();
CountryRating cr1 = new CountryRating("USA", 1);
CountryRating cr2 = new CountryRating("India", 2);
CountryRating cr3 = new CountryRating("Pakistan", 3);
list.add(cr1);
list.add(cr2);
list.add(cr3);
Collections.sort(list, new CountrySort());
printList(list);
System.out.println(" ");
Collections.sort(list, new RatingSort());
printList(list);
}
private static void printList(List<CountryRating> list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
class CountrySort implements Comparator<CountryRating> {
#Override
public int compare(CountryRating cr1, CountryRating cr2) {
return cr1.getName().compareTo(cr2.getName());
}
}
class RatingSort implements Comparator<CountryRating> {
#Override
public int compare(CountryRating cr1, CountryRating cr2) {
return cr1.getRating() - cr2.getRating();
}
}

Implement reading of contact from phone book Using Hashtable in J2ME

I ran into a bind whereby I had to sort the data read from the phones PIM. In doing this I lost the other to which each contact field was referenced to the telephone number because I made use of 2 separate vectors as illustrated below
Before sorting
Nna - +445535533
Ex - +373773737
Ab - +234575757
After sorting.(Which shouldn't be)
Ab - +445535533
Ex - +373773737
Nna - +234575757
This gives an undesired behavior since the sort removes the index to index pointer of the vectors and a selected name (in a Multiple list Box) will get a wrong number.
Alternatively,
I used a hashtable, with the intention of using the names as keys and numbers as the values.
But this pairing means duplicate names being used as keys will not be allowed. Thus I made it a i.e the phone number as keys instead.
I don't want to sound like a cry baby so I stop here for a while and so you the code with a hope u guys would understand it
MY QUESTION
1. Is there a better way/algorithm to implement this?
2. How do I implement the getSelectedItems() in such a ways that it grabs the numbers of the selected indexes of a MULTIPLE CHOICE LIST from a hashTable
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import javax.microedition.lcdui.List;
import javax.microedition.pim.Contact;
import javax.microedition.pim.ContactList;
import javax.microedition.pim.PIM;
import javax.microedition.pim.PIMException;
/**
*
* #author nnanna
*/
public class LoadContacts implements Operation {
private boolean available;
private Vector telNames = new Vector();
Vector telNumbers = new Vector();
Hashtable Listcontact = new Hashtable();
private String[] names;
public Vector getTelNames() {
return telNames;
}
public Hashtable getListcontact() {
return Listcontact;
}
public void execute() {
try {
// go through all the lists
String[] allContactLists = PIM.getInstance().listPIMLists(PIM.CONTACT_LIST);
if (allContactLists.length != 0) {
for (int i = 0; i < allContactLists.length; i++) {
System.out.println(allContactLists[i]);
System.out.println(allContactLists.length);
loadNames(allContactLists[i]);
System.out.println("Execute()");
}
} else {
available = false;
}
} catch (PIMException e) {
available = false;
} catch (SecurityException e) {
available = false;
}
}
private void loadNames(String name) throws PIMException, SecurityException {
ContactList contactList = null;
try {
contactList = (ContactList) PIM.getInstance().openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY, name);
// First check that the fields we are interested in are supported(MODULARIZE)
if (contactList.isSupportedField(Contact.FORMATTED_NAME) && contactList.isSupportedField(Contact.TEL)) {
Enumeration items = contactList.items();
Hashtable temp = new Hashtable();
while (items.hasMoreElements()) {
Contact contact = (Contact) items.nextElement();
int telCount = contact.countValues(Contact.TEL);
int nameCount = contact.countValues(Contact.FORMATTED_NAME);
if (telCount > 0 && nameCount > 0) {
String contactName = contact.getString(Contact.FORMATTED_NAME, 0);
// go through all the phone availableContacts
for (int i = 0; i < telCount; i++) {
System.out.println("Read Telno");
int telAttributes = contact.getAttributes(Contact.TEL, i);
String telNumber = contact.getString(Contact.TEL, i);
Listcontact.put(telNumber, contactName);
temp.put(contactName, telNumber);
}
names = getSortedList();
// Listcontact = temp;
System.out.println(temp + "-------");
System.out.println(Listcontact + "*******");
shortenName(contactName, 20);
}
available = true;
}
} else {
available = false;
}
} finally {
// always close it
if (contactList != null) {
contactList.close();
}
}
}
private void shortenName(String name, int length) {
if (name.length() > length) {
name = name.substring(0, 17) + "...";
}
}
public Vector getSelectedItems(List lbx) {
boolean[] arrSel = new boolean[lbx.size()];
Vector selectedNumbers = new Vector();
int selected = lbx.getSelectedFlags(arrSel);
String selectedString;
String result = "";
for (int i = 0; i < arrSel.length; i++) {
if (arrSel[i]) {
selectedString = lbx.getString(lbx.getSelectedFlags(arrSel));
result = result + " " + i;
System.out.println(Listcontact.get(selectedString));
// System.out.println(telNumbers.elementAt(i));
}
}
return selectedNumbers;
}
private String[] sortResults(String data[]) {
RecordSorter sorter = new RecordSorter();
boolean changed = true;
while (changed) {
changed = false;
for (int j = 0; j < (data.length - 1); j++) {
String a = data[j], b = data[j + 1];
if (a != null && b != null) {
int order = sorter.compare(a.getBytes(), b.getBytes());
if (order == RecordSorter.FOLLOWS) {
changed = true;
data[j] = b;
data[j + 1] = a;
}
}
}
}
return data;
}
public String[] getNames() {
return names;
}
Vector elements = new Vector();
private String[] getValueArray(Hashtable value) {
System.out.println(Listcontact + " c");
Enumeration e = value.elements();
while (e.hasMoreElements()) {
elements.addElement(e.nextElement());
}
String[] elementsArray = new String[elements.size()];
elements.copyInto(elementsArray);
elements.removeAllElements();
System.out.println(elementsArray + " k");
return elementsArray;
}
public void getDuplicates(Vector realValue) {
Vector duplicate = new Vector();
Enumeration e = realValue.elements();
for (int i = 0; e.hasMoreElements(); i++) {
if (duplicate.isEmpty() || !duplicate.elementAt(i).equals(e.nextElement())) {
break;
} else {
duplicate.addElement(e.nextElement());
}
}
}
public String[] getSortedList() {
return sortResults(getValueArray(Listcontact));
}
}
Let me reiterate you requirement: You want a method that will sort the contacts read from native phonebook, then alphabetically sort them on name.
Following is the approach,
Replace the vectors and hash-tables in your code with a single vector, say contactListVector, containing elements of type ContactItem, don't worry this class is explained below. Fundamentally the contact's name and number(s) are linked together in a ContactItem, hence you do not have to worry about there mappings which reduces the usage of redundant data structures.
class ContactItem {
private String name;
private String tnumber; //this can also be a data structure
//for storing multiple numbers
ContactItem( String name, String tnumber) {
this.name = name;
this.tnumber = tnumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTnumber() {
return tnumber;
}
public void setTnumber(String tnumber) {
this.tnumber = tnumber;
}
}
You can reuse the sorting algorithm on contactListVector by comparing the member variable ContactItem.name of the vector element. Also you can deploy different sorts on member variables numbers and/or names. Also there are lots of libraries for JavaME available that have better sorting algorithm's implemented if need be use them.
I would recommend you to perform the sorting once on the contactListVector elements at the end of your method loadNames(...) maybe in the finally block triggered by some boolean variable. The current sorting call in each iteration on items enumeration is expensive and time consuming.
Also you can serialize / deserialize the ContactItem thus persist your contact list.
Let me know if you need detailed explanation.
What about inserting the contact name and numbers inside a recordStore , so you can later make a sort by creating a class which implements RecordComparator.
This statement in your code makes no sense:
selectedString = lbx.getString(lbx.getSelectedFlags(arrSel))
Per lcdui List API documentation above will return the string located at the index equal to the number of selected elements why would you need that?
If you need to output selected text for debugging purposes, use lbx.getString(i) instead.
To implement the getSelectedItems() in such a ways that it grabs the numbers of the selected indexes of a MULTIPLE CHOICE LIST do about as follows:
public Vector getSelectedItems(List lbx) {
boolean[] arrSel = new boolean[lbx.size()];
Vector selectedNumbers = new Vector();
int selected = lbx.getSelectedFlags(arrSel);
System.out.println("selected: [" + selected + "] elements in list");
String selectedString;
String result = "";
for (int i = 0; i < arrSel.length; i++) {
if (arrSel[i]) {
// here, i is the selected index
selectedNumbers.addElement(new Integer(i)); // add i to result
String selectedString = lbx.getString(i);
System.out.println("selected [" + selectedString
+ "] text at index: [" + i + "]");
}
}
return selectedNumbers;
}
As for sorting needs, just drop the HashTable and use Vector of properly designed objects instead as suggested in another answer - with your own sorting algorithm or one from some 3rd party J2ME library.
I would suggest you to have Contact class with name and Vector of numbers. And instead of sorting names array sort the array of contacts.

Categories