I have a collections...i wrote code to sort values using my own comparator
my comparator code is
private static class MatchComparator implements Comparator<xmlparse> {
#Override
public int compare(xmlparse object1, xmlparse object2) {
String match1 = object1.getMatchId();
String match2 = object2.getMatchId();
return match1.compareTo(match2);
}
}
I will call Collections.sort(list,new MatchComparator());
Everything is fine but my problem is the sorted list is wrong when i print it...
Input for list
Match19
Match7
Match12
Match46
Match32
output from the sorted list
Match12
Match19
Match32
Match46
Match7
my expected output is
Match7
Match12
Match19
Match32
Match46
to get the order you need, you could either prefix the 1 digit numbers with zero ( eg Match07 ) or you have to split the string in a prefix and a numeric part, and implement the sorting as numeric comparison
The problem is that String.compareTo(..) compares the words char by char.
If all string start with Match, then you can easily fix this with:
public int compare(xmlparse object1, xmlparse object2) {
String match1 = object1.getMatchId();
String match2 = object2.getMatchId();
return Integer.parseInt(match1.replace("Match"))
- Integer.parseInt(match2.replace("Match"));
}
In case they don't start all with Match, then you can use regex:
Integer.parseInt(object1.replaceAll("[a-zA-Z]+", ""));
Or
Integer.parseInt(object1.replaceAll("[\p{Alpha}\p{Punch}]+", ""));
And a final note - name your classes with uppercase, camelCase - i.e. XmlParse instead of xmlparse - that's what the convention dictates.
Implement a function and use it for comparison:
instead of
return match1.compareTo(match2);
use
return compareNatural(match1,match2);
Here is a function which does a natural comparison on strings:
private static int compareNatural(String s, String t, boolean caseSensitive) {
int sIndex = 0;
int tIndex = 0;
int sLength = s.length();
int tLength = t.length();
while (true) {
// both character indices are after a subword (or at zero)
// Check if one string is at end
if (sIndex == sLength && tIndex == tLength) {
return 0;
}
if (sIndex == sLength) {
return -1;
}
if (tIndex == tLength) {
return 1;
}
// Compare sub word
char sChar = s.charAt(sIndex);
char tChar = t.charAt(tIndex);
boolean sCharIsDigit = Character.isDigit(sChar);
boolean tCharIsDigit = Character.isDigit(tChar);
if (sCharIsDigit && tCharIsDigit) {
// Compare numbers
// skip leading 0s
int sLeadingZeroCount = 0;
while (sChar == '0') {
++sLeadingZeroCount;
++sIndex;
if (sIndex == sLength) {
break;
}
sChar = s.charAt(sIndex);
}
int tLeadingZeroCount = 0;
while (tChar == '0') {
++tLeadingZeroCount;
++tIndex;
if (tIndex == tLength) {
break;
}
tChar = t.charAt(tIndex);
}
boolean sAllZero = sIndex == sLength || !Character.isDigit(sChar);
boolean tAllZero = tIndex == tLength || !Character.isDigit(tChar);
if (sAllZero && tAllZero) {
continue;
}
if (sAllZero && !tAllZero) {
return -1;
}
if (tAllZero) {
return 1;
}
int diff = 0;
do {
if (diff == 0) {
diff = sChar - tChar;
}
++sIndex;
++tIndex;
if (sIndex == sLength && tIndex == tLength) {
return diff != 0 ? diff : sLeadingZeroCount - tLeadingZeroCount;
}
if (sIndex == sLength) {
if (diff == 0) {
return -1;
}
return Character.isDigit(t.charAt(tIndex)) ? -1 : diff;
}
if (tIndex == tLength) {
if (diff == 0) {
return 1;
}
return Character.isDigit(s.charAt(sIndex)) ? 1 : diff;
}
sChar = s.charAt(sIndex);
tChar = t.charAt(tIndex);
sCharIsDigit = Character.isDigit(sChar);
tCharIsDigit = Character.isDigit(tChar);
if (!sCharIsDigit && !tCharIsDigit) {
// both number sub words have the same length
if (diff != 0) {
return diff;
}
break;
}
if (!sCharIsDigit) {
return -1;
}
if (!tCharIsDigit) {
return 1;
}
} while (true);
} else {
// Compare words
// No collator specified. All characters should be ascii only. Compare character-by-character.
do {
if (sChar != tChar) {
if (caseSensitive) {
return sChar - tChar;
}
sChar = Character.toUpperCase(sChar);
tChar = Character.toUpperCase(tChar);
if (sChar != tChar) {
sChar = Character.toLowerCase(sChar);
tChar = Character.toLowerCase(tChar);
if (sChar != tChar) {
return sChar - tChar;
}
}
}
++sIndex;
++tIndex;
if (sIndex == sLength && tIndex == tLength) {
return 0;
}
if (sIndex == sLength) {
return -1;
}
if (tIndex == tLength) {
return 1;
}
sChar = s.charAt(sIndex);
tChar = t.charAt(tIndex);
sCharIsDigit = Character.isDigit(sChar);
tCharIsDigit = Character.isDigit(tChar);
} while (!sCharIsDigit && !tCharIsDigit);
}
}
}
a better one is here
The comparison is lexicographic and not numerical, that's your problem. In lexicoraphic ordering, 10 comes before 9.
See this question for open source implementation solutions. You can also implement your own string comparison, which shouldn't be that hard.
It seems that you expect not what the String.compareTo() really is. It performs so called lexicographical comarsion, but you try to compare it by number. You need to modify the code of your comparator.
#Override
public int compare(xmlparse object1, xmlparse object2) {
String match1 = object1.getMatchId();
String match2 = object2.getMatchId();
Long n1 = getNumber(match1);
Long n2 = getNumber(match2);
return n1.compareTo(n2);
}
where getNumber() extracts the last nuber from string "matchXX"
Related
I have no idea how to check if char[] contains only one letter (a or b) on the first position and only one int (0-8) on the second position. for example a2, b2
I have some this, but I do not know, what should be instead of digital +=1;
private boolean isStringValidFormat(String s) {
boolean ret = false;
if (s == null) return false;
int digitCounter = 0;
char first = s.charAt(0);
char second = s.charAt(1);
if (first == 'a' || first == 'b') {
if (second >= 0 && second <= '8') {
digitCounter +=1;
}
}
ret = digitCounter == 2; //only two position
return ret;
}
` public char[] readFormat() {
char[] ret = null;
while (ret == null) {
String s = this.readString();
if (isStringValidFormat(s)) {
ret = s.toCharArray();
}else {
System.out.println("Incorrect. Values must be between 'a0 - a8' and 'b0 - b8'");
}
}
return new char[0];
}`
First, I would test for null and that there are two characters in the String. Then you can use a simple boolean check to test if first is a or b and the second is between 0 and 8 inclusive. Like,
private boolean isStringValidFormat(String s) {
if (s == null || s.length() != 2) {
return false;
}
char first = s.charAt(0);
char second = s.charAt(1);
return (first == 'a' || first == 'b') && (second >= '0' && second <= '8');
}
For a well understood pattern, use Regex:
private static final Pattern pattern = Pattern.compile("^[ab][0-8]$")
public boolean isStringValidFormat(String input) {
if (input != null) {
return pattern.matcher(input).matches();
}
return false;
}
i have a class in a java program where i am using a toString function to retrieve data. the toString checks a private function in the same class which returns a int value, for displaying different types of return messages.~
The problem is that if i use a local variable in the string function every turns out good, but if i check in the if statements directlly the private function, this function doesnt return any value.
private int computerTryHorizontalPlay() {
int repeatedMyValueCount = 0;
int repeatedYourValueCount = 0;
int[] myPositions = new int[3];
int[] yourPositions = new int[3];
for (int a = 0; a < 3; a++) {
int repeatedMyValue = 0;
int repeatedYourValue = 0;
int emptyFields = 0;
int[] emptyPosition = new int[2];
for (int b = 0; b < 3; b++) {
if (jogoGalo[a][b] == 'X') {
repeatedMyValue++;
} else if (jogoGalo[a][b] == 'O') {
repeatedYourValue++;
}
if (jogoGalo[a][b] == '-') {
emptyPosition[0] = a;
emptyPosition[1] = b;
emptyFields++;
}
}
if (repeatedMyValue == 3 || repeatedYourValue == 3) {
return 3;
} else {
if (emptyFields == 1) {
if (repeatedMyValue == 2) {
repeatedMyValueCount++;
myPositions[repeatedMyValueCount - 1] = emptyPosition[0];
myPositions[repeatedMyValueCount] = emptyPosition[1];
} else if (repeatedYourValue == 2) {
repeatedYourValueCount++;
yourPositions[repeatedYourValueCount - 1] = emptyPosition[0];
yourPositions[repeatedYourValueCount] = emptyPosition[1];
}
}
}
}
if (repeatedMyValueCount > 0) {
jogoGalo[myPositions[0]][myPositions[1]] = 'X';
return 2;
} else if (repeatedYourValueCount > 0) {
jogoGalo[yourPositions[0]][yourPositions[1]] = 'X';
return 1;
}
return 0;
}
This doesn´t work!
public String toString() {
if(computerTryHorizontalPlay() == 3) {
return "The game has already ended!";
}
else if(computerTryHorizontalPlay() == 2) {
return "Computer won!";
}
else if(computerTryHorizontalPlay() == 1) {
return "Computer defendeu!";
}
return null;
}
This works!
public String toString() {
int horizontalFunctionValue = computerTryHorizontalPlay();
if(horizontalFunctionValue == 3) {
return "The game has already ended!";
}
else if(horizontalFunctionValue == 2) {
return "Computer won!";
}
else if(horizontalFunctionValue == 1) {
return "Computer defendeu!";
}
return null;
}
}
toString() must be a read-only method, i.e. it is not allowed to have side-effects like changing the state of the object. Since computerTryHorizontalPlay() is a state-changing method, you are not allowed to call it from toString().
Since the only state-change happens in the last if statement, you can change the code to not execute the play when called from toString(), like this:
private int computerTryHorizontalPlay() {
return computerTryHorizontalPlay(true);
}
private int computerTryHorizontalPlay(boolean doMove) {
// lots of code here
if (repeatedMyValueCount > 0) {
if (doMove)
jogoGalo[myPositions[0]][myPositions[1]] = 'X';
return 2;
} else if (repeatedYourValueCount > 0) {
if (doMove)
jogoGalo[yourPositions[0]][yourPositions[1]] = 'X';
return 1;
}
return 0;
}
public String toString() {
if(computerTryHorizontalPlay(false) == 3) {
return "The game has already ended!";
}
else if(computerTryHorizontalPlay(false) == 2) {
return "Computer won!";
}
else if(computerTryHorizontalPlay(false) == 1) {
return "Computer defeated!";
}
return null;
}
I'm trying to verify if a String s match/is a real number. For that I created this method:
public static boolean Real(String s, int i) {
boolean resp = false;
//
if ( i == s.length() ) {
resp = true;
} else if ( s.charAt(i) >= '0' && s.charAt(i) <= '9' ) {
resp = Real(s, i + 1);
} else {
resp = false;
}
return resp;
}
public static boolean isReal(String s) {
return Real(s, 0);
}
But obviously it works only for round numbers. Can anybody give me a tip on how to do this?
P.S: I can only use s.charAt(int) e length() Java functions.
You could try doing something like this. Added recursive solution as well.
public static void main(String[] args) {
System.out.println(isReal("123.12"));
}
public static boolean isReal(String string) {
boolean delimiterMatched = false;
char delimiter = '.';
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (!(c >= '0' && c <= '9' || c == delimiter)) {
// contains not number
return false;
}
if (c == delimiter) {
// delimiter matched twice
if (delimiterMatched) {
return false;
}
delimiterMatched = true;
}
}
// if matched delimiter once return true
return delimiterMatched;
}
Recursive solution
public static boolean isRealRecursive(String string) {
return isRealRecursive(string, 0, false);
}
private static boolean isRealRecursive(String string, int position, boolean delimiterMatched) {
char delimiter = '.';
if (position == string.length()) {
return delimiterMatched;
}
char c = string.charAt(position);
if (!(c >= '0' && c <= '9' || c == delimiter)) {
// contains not number
return false;
}
if (c == delimiter) {
// delimiter matched twice
if (delimiterMatched) {
return false;
}
delimiterMatched = true;
}
return isRealRecursive(string, position+1, delimiterMatched);
}
You need to use Regex. The regex to verify that whether a string holds a float number is:
^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$
Can anybody give me a tip on how to do this?
Starting with your existing recursive matcher for whole numbers, modify it and use it in another method to match the whole numbers in:
["+"|"-"]<whole-number>["."[<whole-number>]]
Hint: you will most likely need to change the existing method to return the index of that last character matched rather than just true / false. Think of the best way to encode "no match" in an integer result.
public static boolean isReal(String str) {
boolean real = true;
boolean sawDot = false;
char c;
for(int i = str.length() - 1; 0 <= i && real; i --) {
c = str.charAt(i);
if('-' == c || '+' == c) {
if(0 != i) {
real = false;
}
} else if('.' == c) {
if(!sawDot)
sawDot = true;
else
real = false;
} else {
if('0' > c || '9' < c)
real = false;
}
}
return real;
}
Why subsequence(a,b).toString() is faster than substring(a,b)?
when i convert my all subsequences to substring it slows up to %7 all the time. why does it happen?
Below is my code;
private static String filterStr(String s)
{
for(int a = 0; a < s.length(); a++)
{
int c = s.charAt(a);
if(((c < 65) || ((c >90) &&(c < 97)) || (c > 122)))
{
if(c!=34 && c!=96 && c!=39)// tırnak değillerse
{
String temp = s.substring(0,a);
temp+= s.subSequence(a+1,s.length());
s = temp;
a--;
}
else
{
if(a !=0) // if not at the beginning
{
if(a == s.length()-1)
s = s.subSequence(0,s.length()-1).toString();
else
s = s.subSequence(0,s.length()-2).toString();
}
else
s = s.subSequence(1,s.length()).toString();
}
}
if(c >= 65 && c <= 90) // convert to lower case first character.
{
String temp = s.substring(1,s.length());
c+=32;
s = (char)c + temp;
}
}
return s;
}
CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
this is the implementation of subSequence method, It can not be faster/slower.
I have the following function.
private boolean codeContains(String name, String code) {
if (name == null || code == null) {
return false;
}
Pattern pattern = Pattern.compile("\\b" + Pattern.quote(name) + "\\b");
Matcher matcher = pattern.matcher(code);
return matcher.find();
}
It is called many thousand times in my code, and is the function in which my program spends the most amount of time in. Is there any way to make this function go faster, or is it already as fast as it can be?
If you don't need to check word boundaries, you might do this :
private boolean codeContains(String name, String code) {
return name != null && code != null && code.indexOf(name)>=0;
}
If you need to check word boundaries but, as I suppose is your case, you have a big code in which you often search, you could "compile" the code once by
splitting the code string using the split method
putting the tokens in a HashSet (checking if a token is in a hashset is reasonably fast).
Of course, if you have more than one code, it's easy to store them in a structure adapted to your program, for example in a map having as key the file name.
"Plain" string operations will (almost) always be faster than regex, especially when you can't pre-compile the pattern.
Something like this would be considerably faster (with large enough name and code strings), assuming Character.isLetterOrDigit(...) suits your needs:
private boolean codeContains(String name, String code) {
if (name == null || code == null || code.length() < name.length()) {
return false;
}
if (code.equals(name)) {
return true;
}
int index = code.indexOf(name);
int nameLength = name.length();
if (index < 0) {
return false;
}
if (index == 0) {
// found at the start
char after = code.charAt(index + nameLength);
return !Character.isLetterOrDigit(after);
}
else if (index + nameLength == code.length()) {
// found at the end
char before = code.charAt(index - 1);
return !Character.isLetterOrDigit(before);
}
else {
// somewhere inside
char before = code.charAt(index - 1);
char after = code.charAt(index + nameLength);
return !Character.isLetterOrDigit(after) && !Character.isLetterOrDigit(before);
}
}
And a small test succeeds:
#Test
public void testCodeContainsFaster() {
final String code = "FOO some MU code BAR";
org.junit.Assert.assertTrue(codeContains("FOO", code));
org.junit.Assert.assertTrue(codeContains("MU", code));
org.junit.Assert.assertTrue(codeContains("BAR", code));
org.junit.Assert.assertTrue(codeContains(code, code));
org.junit.Assert.assertFalse(codeContains("FO", code));
org.junit.Assert.assertFalse(codeContains("BA", code));
org.junit.Assert.assertFalse(codeContains(code + "!", code));
}
This code seemed to do it:
private boolean codeContains(String name, String code) {
if (name == null || code == null || name.length() == 0 || code.length() == 0) {
return false;
}
int nameLength = name.length();
int lastIndex = code.length() - nameLength;
if (lastIndex < 0) {
return false;
}
for (int curr = 0; curr < lastIndex; ) {
int index = code.indexOf(name, curr);
int indexEnd = index + nameLength;
if (index < 0 || lastIndex < index) {
break;
}
boolean leftOk = index == curr ||
index > curr && !Character.isAlphabetic(code.charAt(index - 1));
boolean rightOk = index == lastIndex ||
index < lastIndex && !Character.isAlphabetic(code.charAt(indexEnd));
if (leftOk && rightOk) {
return true;
}
curr += indexEnd;
}
return false;
}
The accepted answer goes to dystroy as he was the first to point me in the right direction, excellent answer by Bart Kiers though, +1!