Finding information stored in array by binary search - java

This is a small library with two books for the sake of the question, it allows the user to type in a random number, and if that number matches up with a book the title of the book is outputted. I've created a class called 'Book' which houses all the titles.
String book1, book2;
class Book {
Book (int _input, String book_1, String book_2) {
book1 = book_1 = "Read This Book";
book2 = book_2 = "How to Read a Book";
I apologize if my code is all one big mess that makes no sense...
}
}
ArrayList <Book> titles = new ArrayList <Book>(50);
public static Boolean binarySearch(String [] A, int left, int right, String V) { //binary search
int middle;
Boolean found = false;
while (found == false && left <= right) {
//If middle item == 0, returns true
middle = (left + right)/2;
int compare = A[middle].compareTo(V);
if (compare == 0) {
found = true;
} else {
if (compare >0) {
right = middle -1;
} else {
left = middle + 1;
}
}
}
if (left > right) {
return false;
} else {
return true;
}
}
Then the problem...I'm not sure how to use the binary search to actually output any information after pressing the "find" button, any ideas on what I should below to make this work?
private void findButtonActionPerformed(java.awt.event.ActionEvent evt) {
//Take inputted values which will match with book title
int input = Integer.parseInt(enterNumberField.getText());
//Store values in array
Book c = new Book (input, book1, book2);
titles.add(c);
String temp;
//calls out information in array
for (int j=0; j<=input; j++) {
for (int x=0; x<=input; x++) {
temp = titles.get(x) + "\n";
}
binarySearchField.setText("" + j); //should output book title
}

You want your binary search to return not just a true or false. You want it to return Book, the item it found, or null if it found no book matching this query. To be consistent you probably want to change the name from binarySearch, to getBook, or some other better suited name. In your case you don't want to know if an element is there, you want to get the element for use later (printing).
This is how collections are expected to behave when you query them. Just check out the get methods from any of the Java collections and you will see they do the same, returning the item if it's there, or null.
Here is some example code. This is just example code! So modify as you like, and also be careful about bugs, I used your search which I'm going to assume is correct to start with. Also know that there are better many good ways of storing a key to a value, Map for example, that I'm not going to use here.
public class Book{
public String title;
public int sameTitle(String bookTitle) {
return this.title.compareTo(bookTitle);
}
}
public static Book getBook(Book [] A, int left, int right, String bookTitle) { //binary search
int middle;
while (left <= right) {
//If middle item == 0, returns true
middle = (left + right)/2;
int compare = A[middle].sameTitle(bookTitle);
if (compare == 0) {
return A[middle];
} else {
if (compare >0) {
right = middle -1;
} else {
left = middle + 1;
}
}
}
return null;
}
// example use of getting and using the book
Book b = getBook(...);
if (b != null){
System.out.println("Success! you found the book " + b);
}

Try to change this line:
int compare = A[middle].compareTo(V);
if (compare == 0) {
found = true;
To:
int compare = A[middle].compareTo(V);
if (compare == 0) {
return A[middle];
And be sure to get the result in your findButtonActionPerformed method.
Also, it appears to be a mistake in your code... Should not A be a book array instead of a string array?

Related

Adding an object to a sorted array into the correct spot

So I have this project and im writing the add method for my catalog class and this add method needs to add an item to a sorted array into the right place using insertion sort, unless the array has nothing in it in that case i just want to add it in normally. this whole project must use an array I cannot use an arraylist or anything else.
The problem I am having here is that the way my program currently is, its only adding one object to my array and each time i try to add a new one during run tine it jst replaces the item already in there. I know that my problem is something in the body of my while loop and the way i initialize my position variable.
here is the method im having trouble with.
public void addItem(Item theItem)
{
int position = size;
if(size != 0){
while (position > 0 && theItem.compareTo(items[position - 1]) < 0){
items[position] = items[position - 1];
position--;
}
items[position] = theItem;
}
else{
items[size] = theItem;
size++;
}
here is my compareTo method
public int compareTo(Item other){
if(this.getItemType().equals(other.getItemType())){
return this.itemnum - other.itemnum;
}
//item types are not equal
else
return this.getItemType().compareTo(other.getItemType());
//try writing code to compare by price as well
}
The most likely problem in your code is this line:
items[position-1] = items[position];
This will copy an item in you array from the current position to the position to the left of it.
When you insert a new item you want to copy items from the left to the current position to make room for the new item to the left.
Change it to
items[position] = items[position-1];
A size++ is also missing after the while block, inside the first if block.
I realized this when adding a second call to addItem in my test code below.
You could also put a single size++ statement outside of the if statement.
A Complete, Minimal, Reproducible Example that I used trying to fix it. I have used Integer instead of Item to avoid having to add more classes.
public class Main {
private int size = 0;
private Integer[] items = new Integer[20];
public static void main(String... args) {
new Main().execute(); // Moving us into a non-static context
}
public void execute() {
System.arraycopy(new Integer[] {1,2,3,4,6,7,8,9}, 0, items, 0, 8);
size = 8;
// items = [1,2,3,4,6,7,8,9,null,null,...]
addItem(5);
addItem(5); // test adding a second item
// items = [1,2,3,4,5,6,7,8,9,null,null,...]
for (Integer i : items) {
System.out.println(i);
}
}
public void addItem(Integer item) {
int position = size;
if (size != 0) {
while (position > 0 && item.compareTo(items[position - 1]) < 0) {
// items[position-1] = items[position]; // Result [1,2,3,4,5,null,null,...]
items[position] = items[position-1]; // Result [1,2,3,4,5,6,7,8,9,null,null,...]
position--;
}
items[position] = item;
size++; // this line was missing as well
} else {
items[size] = item;
size++;
}
// or a single size++; here, removing the other two
}
}
The ugly solution by making new array
public int[] addItem(int item, int[] items){
int[] tempArr = new int[items.length + 1];
boolean hasAlready = false;
for(int i = 0 ; i < items.length; i++){
if(hasAlready)tempArr[i + 1] = items[i];
else if(item < items[i]){
tempArr[i] = item;
tempArr[i + 1] = items[i];
hasAlready = true;
}else {
tempArr[i] = items[i];
}
}
//items = tempArr; if items is global variable
return tempArr;
}
One can use existing utility functions, Arrays.binarySearch, and System.arraycopy. Your loop was 1 off.
public void addItem(Item theItem) {
Comparator<Item> comparator = Comparator.comparing(Item::getItemType)
.thenComparingInt(it -> it.itemnum);
int position = Arrays.binarySearch(items, 0, size, theItem, comparator);
// If position >= 0 the item was found (maybe no need to insert?)
if (position < 0) {
position = ~position; // Insert position of not found item
}
System.arraycopy(items, position, items, position + 1, size - position);
items[position] = theItem;
size++;
}
Binary search results in the non-negative index when found, or the negative ~index when not found. Here binary search is done on a subarray from 0 upto size (excluded).
Same as Roger Gustavsson
public class Main {
private int size = 0;
private Integer[] items = new Integer[20];
public static void main(String... args) {
new Main().execute(); // Moving us into a non-static context
}
public void execute() {
System.arraycopy(new Integer[] {1,2,3,4,6,7,8,9}, 0, items, 0, 8);
size = 8;
// items = [1,2,3,4,6,7,8,9,null,null,...]
addItem(5);
// items = [1,2,3,4,5,6,7,8,9,null,null,...]
for (Integer i : items) {
System.out.println(i);
}
}
public void addItem(Integer item) {
if (size == 0) {
items[size] = item;
size++;
return;
}
int position = size;
while (position > 0 && item.compareTo(items[position - 1]) < 0) {
items[position] = items[position - 1];
position--;
}
items[position] = item;
size++;
}
}
on what you are trying to achieve, i think next solution will be starting point from where you can build your own solution depending your specific needs. i have Changed your main method a little bit, and i do not know if your classes implements comparable /Comparator or not.
public void addItem(Item theItem) {
int position = position(items, theItem); // position is a method that finds best position for inseriton
if (items[position] == null){ // if items at best position is null then add new element there
items[position] = theItem;
} else{
items[size] = theItem; // if not add element at last position
swapUp(size); // and swap them up to perfect position.
}
size++;
}
method that find best position looks like this.
private static int position(Item[] items, Item newItem) {
if (isEmpty(items))
return 0;
int pos=0;
int target=items.length-1;
while(pos < target){
int m = pos+(target-pos)/2;
if (items[m] !=null){
if(newItem.getNumber()>items[m].getNumber()){ // comparing depending on item number
pos=m+1;
}else{
target=m;
}
}else{
target = m;
}
}
return pos;
}
as you can see method is looking for position depending on item number, you can change this with your type, or do both type and number comparison. Swaping up is handled by thus 2 method.
private void swapUp(int lastPosition){
if (lastPosition == -1){
return;
}
Item lastItem = items[lastPosition];
Item p = items[lastPosition-1];
if (lastItem.getNumber() < p.getNumber())
replace(lastPosition, lastPosition-1);
else
lastPosition = 0;
swapUp(lastPosition-1);
}
private void replace(int from, int to){
Item temporary = items[from];
items[from] = items[to];
items[to] = temporary;
}
and again i'm doing comparison of numbers you can implement any kind of comparison you want. i saw your previous question and modeled your classes
Music{number=1111, name='White and Nerdy', price=2.5, pro='"Weird Al" Yankovic'}
Music{number=2222, name='Amish Paradise', price=2.22, pro='"Weird Al" Yankovic'}
Music{number=3333, name='The Saga Begins', price=2.0, pro='"Weird Al" Yankovic'}
Movie{number=4444, name='UHF', price=9.99, pro='"Weird Al" Yankovic'}
Movie{number=5555, name='The Dark Crystal', price=8.99, pro='"Jim Henson'}
Movie{number=6666, name='Die Hard', price=13.99, pro='Bruce Willis'}
Movie{number=6969, name='The Adventures of Mr. Winky', price=9.99, pro='Richard Dickinson'}
Book{number=7777, name='When I Grow Up', price=7.98, pro='"Weird Al" Yankovic'}
Book{number=8888, name='The Chronicles of Pern: First Fall', price=5.99, pro='"Anne McCaffrey'}
Book{number=9999, name='Get gud you scrub', price=2.5, pro='Steve "Troll" Rathier'}
as you can see they are in sorted order.

Binary search trouble

so i'm trying to do a binary search through an array of DVD objects by movie director and i'm having a little trouble. when i run my binary search it only says that the director isn't in the movie collection when it is. i'm still not the best at searching yet so any suggestions to point me in the right direction would be appreciated.
public int binarySearch(String key) {
int low=0,high=collection.length-1,mid=(low+high)/2;
while (low <= high && collection[mid].getDirector().compareTo(key)!=0) {
if (key.compareTo(collection[mid].getDirector())>0){
low = mid + 1;
}
else {
high = mid - 1;
}
mid=(low+high)/2;
}
if (low>high){
System.out.print("the director is not in your dvd collection");
return -1;
}
else
System.out.print("the movie by director " + collection[mid].getDirector() + " is in index ");
return mid;
}
First of all,
make sure that your array is sorted by director, for example:
Comparator<DVD> comparator = Comparator.comparing(DVD::getDirector);
Arrays.sort(collection, comparator);
Then, use the binary search of the JDK:
int index = Arrays.binarySearch(collection, new DVD() {
#Override
String getDirector() {
return key;
}
}, comparator);
Thanks #Boris for simplifying my clumsy lambda!

How to Make Program flow control jump back to a former loop in java?

So I have written a code that allows a user to find a word in a TextArea. I have nearly succeeded but for one thing. here, I will show you all the code and tell my problem.
if(ta.getText().length() != 0 && t1.getText().length() != 0)
{
char c1[] = ta.getText().trim().toCharArray();
char c2[] = t1.getText().trim().toCharArray();
for(int i=startFlag;i<c1.length;i++)
{
if(c1[i]==c2[0])
{
start = i;
break;
}
}
k=start;
for(int i=0;i<c2.length;i++)
{
if(c2[i] != c1[start++])
{
}
else
countFlag++;
}
if(countFlag==c2.length)
{
ta.select(k,c2.length);
startFlag++;
}
}
For reference, ta is the TextArea and t1 is the TextField where the user enters a word to find. i have a problem in the second for loop. What should I write in the if () block there so that whenever c2[i] != c1[start++] the control is shifted to the first for loop, that would again determine the value of start?
Create a method to get "start" that you can then call whenever you want.
if(ta.getText().length() != 0 && t1.getText().length() != 0)
{
char c1[] = ta.getText().trim().toCharArray();
char c2[] = t1.getText().trim().toCharArray();
k=getStart(startFlag, c1.length);
for(int i=0;i<c2.length;i++)
{
if(c2[i] != c1[start++])
{
start = getStart(startFlag, c1.length);
}
else
countFlag++;
}
if(countFlag==c2.length)
{
ta.select(k,c2.length);
startFlag++;
}
}
And getStart() is:
public int getStart(int startFlag, int length) {
for(int i=startFlag;i<length;i++)
{
if(c1[i]==c2[0])
{
return i;
}
}
}
You may need different inputs to getStart(), but hopefully this gets across the general idea.
The way your code is set up right now, what you're asking for is impossible. To do what you're asking, you'll need to refactor your current code into different methods. More specifically, you'll need to put the for loops into their own methods and then call these methods.
So what you would need to do is make a separate method for the for loop.
public static int startForLoop(int i) {
for(int i=startFlag;i<c1.length;i++)
{
if(c1[i]==c2[0])
{
start = i;
break;
}
}
}
Then you can just call startForLoop(0) initially and in the 2nd for loops if statment:
if(c2[i] != c1[start++])
{
startForLoop(start+1)
}
This will continue the for loop where it left off. If you need to run the 2nd for loop again then you have to make a separate method for it as well and basically place both of them in a while loop that continues till you find the result you want in the 2nd for loop.
May be this code piece help you what you are looking for.
Basically it moves along with the string to be searched in keeping in mind the index of the string to be searched for.
Sorry but i have implemented it in java, but the notion is same and the result returned is the best what i got.you must give it a try!
private static String searchString(String searchIN,String searchFOR){
if (searchFOR != "") {
int index = searchIN.toUpperCase().indexOf(searchFOR.toUpperCase());
String before = "";
String highlighted = "";
String after = "";
while (index >= 0) {
int len = searchFOR.length();
before = searchIN.substring(0, index);
highlighted = "\"" + searchFOR + "\"";//do what ever you want to do with searched string
after = searchIN.substring(index + len);
searchIN = before + highlighted + after;
index = searchIN.toUpperCase().indexOf(searchFOR.toUpperCase(), index + highlighted.length());
}
}
return searchIN;
}

How to sort a linked list with objects and private variables

I am going to be honest and up front here. This is homework, but I have become desperate and am looking for anyone to assist me. I have been working on this off and on for over a month and have gone to my instructor multiple times. Basically this program needs to create and sort a linked list that has an int, string and double in each node. It needs to be able to sort by each data type as well as print in input order but once I figure one out I can transfer it to the other data types. Please, everything needs to be "hand made", please do not use any built in commands as I need to create everything as per my instructor's demands.
I attempted to make the linked list and then sort it, but I ran into a problem so I decided to try and sort the list as I create it.
For example: Input the first node, then input the next node in front/behind the first, then put the next where it needs to go... and so forth.
Here is my code (I only focus on the strings):
String repeat = "y";
list1 fChr = null;
list1 p = fChr;
list1 copy = null;
//list1 dCopy = null;
//list1 iCopy = null;
list1 fd = fChr;//front of the double list
list1 fi = fChr;//front of the integer list
list1 fStr = fChr;//front of the string list~
list1 pStr = fStr;
boolean inserted = false;
int iii = 0;
String sss = "";
double ddd = 0.0;
while(repeat.equals("y"))//while the user agrees to adding a new node
{
if(fChr == null)// if the front is empty
{
fChr = new list1();//create a new node by calling object and sets it as the front
p = fChr;
copy = fChr;
sss = fChr.GetS();
iii = fChr.GetI();
ddd = fChr.GetD();
copy.SetS(sss);
copy.SetI(iii);
copy.SetD(ddd);
System.out.println("(1)");
}
else
{
System.out.println("(2)");
if(p!=null)
{
System.out.println("p = "+ p.GetS());
if(p.next != null)
{
System.out.println("p.next = "+ p.next.GetS());
System.out.println("p.next.next = "+ p.next.next.GetS());
}
}
p = fChr;
while(p.next != null)//finds the end of the Linked list
{
System.out.println("(3)");
p = p.next;//moves the pointer p down the list
}
list1 NextNode = new list1();//
p.next = NextNode;
sss = NextNode.GetS();
iii = NextNode.GetI();
ddd = NextNode.GetD();
copy = NextNode;
String gg = "hi";//tests to see if the setter is actually changing the value inside copy(it is not, it prints b)
copy.SetS(gg);
copy.SetI(iii);
copy.SetD(ddd);
System.out.println(copy.GetS());
System.out.println("p = "+ p.GetS());
}
pStr = fStr;
//System.out.println(copy.GetS()+"*");
inserted = false;
if(fStr == null)
{
System.out.println("(4)");
fStr = copy;//fStr = fChr;
inserted = true;
//System.out.println("p.next.next = "+ p.next.next.GetS());
}
else if(copy.GetS().compareTo(fStr.GetS()) < 0)
{
System.out.println("(5)");
//System.out.println("1)p.next.next = "+ p.next.next.GetS());
copy.next = fStr;//ERROR ON THIS LINE
System.out.println("2)p.next.next = "+ p.next.next.GetS());
System.out.println("fChr.next: "+fChr.next.GetS());
fStr = copy;
System.out.println("3)p.next.next = "+ p.next.next.GetS());
inserted = true;
System.out.println("p = "+ p.GetS());
System.out.println("p.next = "+ p.next.GetS());
System.out.println("4)p.next.next = "+ p.next.next.GetS());
}
else if(fStr.next == null && fStr != null)
{
System.out.println("(6)");
fStr.next = copy;
inserted = true;
}
else
{
System.out.println("(7)");
pStr = fStr;
System.out.println("RIP (8)");
while(pStr.next != null && inserted == false)
{
System.out.println("(9)");
System.out.println("RIP");
if(copy.GetS().compareTo(pStr.next.GetS()) < 0)//if it goes between 2 nodes
{
System.out.println("(10)");
copy.next = pStr.next;
pStr.next = copy;
inserted = true;
}
else
{
System.out.println("(11)");
pStr = pStr.next;
}
if(pStr.next == null && inserted == false)// it goes at the end(not necessary bc of the (in order) part)
{
System.out.println("(12)");
pStr.next = copy;
}
}
}
repeat = JOptionPane.showInputDialog("Would you like to add a node [y/n]");
System.out.println("End of Loop");
}
System.out.println(fStr.GetS());
PrintMenu(fChr, fi, fd, fStr);// sends the user to the menu screen
}
From all of my print statements I have (what I think) found the problem. This code runs through twice and upon hitting "y" for the third time, prints "(3)" in an infinite loop. I have found that (say the input for the strings is "c" then "b") "p" is equal to "c", p.next is equal to "b" and p.next.next is equal to "c". So, p is in an infinite loop. I have no idea why it does this, I have a theory that it could be because the front(fChr) changes and then "p" points to it and is just kinda drug along. I also just realized that me trying to set "copy" equal to "NextNode" was unsuccessful and copy just holds the value inside p.next(which is NextNode). That seems correct, but when I try to put something else in, it doesn't work. I could be testing this incorrectly and in that case the setter is correct. Setting is one of the main problems that I seem to be having. I will try to answer as many questions as I can if anyone has any.
Also here is the object in case you would like to see it. Thank you for your time, any help will be appreciated. Please if possible try to keep it relatively simple this is a high school assignment and I am so close and am stumped on how to fix what is wrong. Also, you may have noticed, but I have to use private variables. I am not asking for someone to give me a program that works, I am just asking if you know why what is going wrong is happening and if you know how to fix it. Thank you from the bottom of my heart!
import javax.swing.JOptionPane;
public class list1
{
private int i;
private String s;
private double d;
private String ss = null;
private int ii = 0;
private double dd = 0.0;
list1 next = null;
public list1()
{
String str;
s=JOptionPane.showInputDialog("Enter a String");
String temp =JOptionPane.showInputDialog("Enter an Integer");
i = Integer.parseInt(temp);
String temp2 =JOptionPane.showInputDialog("Enter a Double");
d = Double.parseDouble(temp2);
}
public double GetD()
{
return d;
}
public String GetS()
{
return s;
}
public int GetI()
{
return i;
}
public void SetS(String x)
{
ss = x;
}
public void SetI(int y)
{
ii = y;
}
public void SetD(double z)
{
dd = z;
}
}

Sort algorithm problems on java comparable

I want to do a specific sort. I am using java's comparable interface which means the return of my compare method must return -1 +1 or 0 depending on the equality of the two compared, then I am sorting using Collections. My trouble comes from how I wish to compare.
I have a key that is made up of either of the following
[keyName]
[siteName].[keyName]
[siteName].[pageName].[keyName]
so as an example "mysite.alampshade.color"
the tricky part is the sites must be sorted first, followed by keyname, followed by pageName. but firstly by the keynames, then site name, in the order of the number of sections to the property. Sorry. its a little complicated, an example may help. here is the order they must be:
alpha
beta
charlie
sitea.alpha
sitea.charlie
sitea.pagea.beta
sitea.pageb.beta
sitea.pagea.charlie
siteb.alpha
siteb.delta
siteb.pagef.alpha
siteb.pageb.echo
siteb.pageb.golf
siteb.pagea.hotel
siteb.pageb.hotel
siteb.pagec.hotel
I have tried many different ways and have thrown away code a few times but still cant get it perfect. some pseudocode would be of great help if not some java.
EDIT:
to add another possibly simplier to understand example
the following is sorted how I need it
a
b
c
z
a.b
a.c
a.d
a.z
a.b.a
a.c.a
a.b.b
a.b.c
a.c.c
a.a.d
b.a
b.b
b.z
b.a.a
b.b.a
b.a.b
c.c.f
Another option, making it recursive you avoid the problem if there is ever more entries.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class SortTest {
public static void main(String[] args) {
String[] test = new String[]{
"a",
"b",
"b.a",
"b.a.a",
"a.a.a",
"a.b.a",
"a.a",
"a.b",
"b.a.b",
"b.b.a"
};
Arrays.sort(test, new Comparator<String>() {
int compareComplexList(List<String> a, List<String> b, List<int[]> positions, int order ) {
int minimum = a.size() < b.size() ? a.size() - 1 : b.size() - 1;
if (a.get(positions.get(minimum)[order]).compareTo(b.get(positions.get(minimum)[order])) != 0)
return a.get(positions.get(minimum)[order]).compareTo(b.get(positions.get(minimum)[order]));
else if (order < minimum - 1) return compareComplexList(a,b, positions, ++order);
else return Double.compare(a.size(),b.size());
}
public int compare(String a, String b) {
List<String> partsA = Arrays.asList(a.split("\\."));
List<String> partsB = Arrays.asList(b.split("\\."));
List<int[]> orders = new ArrayList<int[]>();
orders.add(new int[] {0});
orders.add(new int[] {0,1});
orders.add(new int[] {0,2,1});
return compareComplexList(partsA, partsB, orders,0);
}
});
System.out.println("Sorted: "+Arrays.toString(test));
}
}
Should be good now.
public int compare(String a, String b) {
String[] partsA = a.split("\\.");
String[] partsB = b.split("\\.");
// If first term is different, we exit.
if (partsA[0].compareTo(partsB[0]) != 0) return partsA[0].compareTo(partsB[0]);
// Else, first term is identical.
else {
// Same number of parts
if (partsA.length == partsB.length) {
// 2 parts, we compare the 2nd part.
if (partsA.length == 2) {
return partsA[1].compareTo(partsB[1]);
// 3 parts, we compare the 3rd part first, then the 2nd part
} else {
if (partsA[2].compareTo(partsB[2]) != 0) return partsA[2].compareTo(partsB[2]);
return partsA[1].compareTo(partsB[1]);
}
// Different number of parts
} else {
// If A has only 1 part, it's first
if (partsA.length == 1) return -1;
// If B has only 1 part, it's first
if (partsB.length == 1) return 1;
// Case 2 vs 3 parts, we compare the 3rd part with the 2nd part of the other. If it's equal, the shorter is first.
if (partsA.length == 3) {
if (partsA[2].compareTo(partsB[1]) != 0) return partsA[2].compareTo(partsB[1]);
else return 1;
} else {
if (partsA[1].compareTo(partsB[2]) != 0) return partsA[1].compareTo(partsB[2]);
else return -1;
}
}
}
}
My other answer started getting too gnarly. Here's a better, more natural solution:
public class StrangeComparator {
private static class Entry implements Comparable<Entry> {
// What to split with.
static final String dot = Pattern.quote(".");
// The parts.
final String key;
final String page;
final String site;
public Entry(String s) {
String [] parts = s.split(dot);
switch (parts.length) {
case 1:
key = parts[0];
page = "";
site = "";
break;
case 2:
key = parts[1];
page = "";
site = parts[0];
break;
case 3:
key = parts[2];
page = parts[1];
site = parts[0];
break;
default:
throw new IllegalArgumentException("There must be at least one part to an entry.");
}
}
#Override
public int compareTo(Entry t) {
int diff = site.compareTo(t.site);
if ( diff == 0 ) {
diff = page.compareTo(t.page);
}
if ( diff == 0 ) {
diff = key.compareTo(t.key);
}
return diff;
}
#Override
public String toString () {
return (site.length() > 0 ? site + "." : "")
+ (page.length() > 0 ? page + "." : "")
+ key;
}
}
public void test() {
String[] test = new String[]{
"alpha",
"beta",
"charlie",
"zeta", // Added to demonstrate correctness.
"sitea.alpha",
"sitea.charlie",
"sitea.pagea.beta",
"sitea.pageb.beta",
"sitea.pagea.charlie",
"siteb.alpha",
"siteb.delta",
"siteb.pagef.alpha",
"siteb.pageb.echo",
"siteb.pageb.golf",
"siteb.pagea.hotel",
"siteb.pageb.hotel",
"siteb.pagec.hotel"
};
Arrays.sort(test);
System.out.println("Normal sort: " + Separator.separate("\n", "\n", test));
Entry[] entries = new Entry[test.length];
for ( int i = 0; i < test.length; i++ ) {
entries[i] = new Entry(test[i]);
}
Arrays.sort(entries);
System.out.println("Special sort: " + Separator.separate("\n", "\n", entries));
}
public static void main(String args[]) {
new StrangeComparator().test();
}
}
Output order is:
alpha
beta
charlie
zeta
sitea.alpha
sitea.charlie
sitea.pagea.beta
sitea.pagea.charlie
sitea.pageb.beta
siteb.alpha
siteb.delta
siteb.pagea.hotel
siteb.pageb.echo
siteb.pageb.golf
siteb.pageb.hotel
siteb.pagec.hotel
siteb.pagef.alpha
Which kinda does what you say but doesn't match your example.
Here's an alternative - if a component is found to contain less that 3 parts then parts are added at the start to take up the slack. It then uses a sort order array to define which columns should be compared next:
public void test() {
String[] test = new String[]{
"alpha",
"beta",
"charlie",
"zeta", // Added to demonstrate correctness.
"sitea.alpha",
"sitea.charlie",
"sitea.pagea.beta",
"sitea.pageb.beta",
"sitea.pagea.charlie",
"siteb.alpha",
"siteb.delta",
"siteb.pagef.alpha",
"siteb.pageb.echo",
"siteb.pageb.golf",
"siteb.pagea.hotel",
"siteb.pageb.hotel",
"siteb.pagec.hotel"
};
Arrays.sort(test);
System.out.println("Normal sort: "+Arrays.toString(test));
Arrays.sort(test, new Comparator<String>() {
// How many columns to pad to.
final int padTo = 3;
// What to pad with.
final String padWith = "";
// What order to compare the resultant columns in.
final int[] order = {0, 2, 1};
#Override
public int compare(String s1, String s2) {
String[] s1parts = padArray(s1.split(Pattern.quote(".")), padTo, padWith);
String[] s2parts = padArray(s2.split(Pattern.quote(".")), padTo, padWith);
int diff = 0;
for ( int i = 0; diff == 0 && i < order.length; i++ ) {
diff = s1parts[order[i]].compareTo(s2parts[order[i]]);
}
return diff;
}
String [] padArray(String[] array, int padTo, String padWith) {
String [] padded = new String[padTo];
for ( int i = 0; i < padded.length; i++ ) {
padded[padded.length - i - 1] = i < array.length ? array[i]: padWith;
}
return padded;
}
});
System.out.println("Special sort: "+Arrays.toString(test));
}
prints (more or less):
Normal sort: [alpha,
beta,
charlie,
sitea.alpha,
sitea.charlie,
sitea.pagea.beta,
sitea.pagea.charlie,
sitea.pageb.beta,
siteb.alpha,
siteb.delta,
siteb.pagea.hotel,
siteb.pageb.echo,
siteb.pageb.golf,
siteb.pageb.hotel,
siteb.pagec.hotel,
siteb.pagef.alpha,
zeta]
Special sort: [alpha,
beta,
charlie,
sitea.alpha,
sitea.charlie,
siteb.alpha,
siteb.delta,
zeta,
siteb.pagef.alpha,
sitea.pagea.beta,
sitea.pageb.beta,
sitea.pagea.charlie,
siteb.pageb.echo,
siteb.pageb.golf,
siteb.pagea.hotel,
siteb.pageb.hotel,
siteb.pagec.hotel]
There does seem to be some ambiguity in your requirements but this code is structured so you can, with trivial tweaks, achieve most interpretations of your comparison quite simply.

Categories