Compare strings bwlow API 18 - java

I searched and got clear that == is not used to compare the content of string variables but equals().
However, AS reports that equals() is only available on API 19 (Android 4.4) and up and I targetted API 18 (my only phone is Android 4.3)
So right now, I'm doing if (var1.contains(var2)) or if (array[i].contains(var)) to compare strings and it works but it doesn't seem correct to me.
What would be the correct way to achieve this on API < 19?
Thanks.
Edit: for clarification (I don't know how to put inline images)
With ==
With equals()
Comparison fails with equals().

The equals function of the Object class was added in Java JDK 1.0.
This version was released on January 23, 1996. It was called 'Oak' back then, so technically it predates even Java itself. (source).
In contrast, Android API 1 was released on September 23, 2008. At this time it would be made with Java JDK 1.5 (latest version was Java SE 5 Update 16).
So in conclusion, equals is available on API level 18, there must be some other error.
After seeing the posted code, you are using Objects.equals(), which is a utility method that checks equals() in a null-safe manner.
In many cases, like yours, you don't need the extra null check because you know at least one of the objects is not null and you can just call equals directly:
if("una".equals(hourNames[realHour]))
Your hourNames array will probably not contain null elements so you should turn it around to the more readable order:
if(hourNames[realHour].equals("una"))

use hourNames[realHour].equals("una")
yeah, that is a bug on android studio (or maybe from intellij idea).

Since if (var1.contains(var2)) works for you why don't you post the exact strings ? In case you don't have debug capability, I would suggest using these to debug the point in difference in the strings:-
boolean contentEquals(CharSequence cs)
public int compareTo(String anotherString)
https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#compareTo(java.lang.String)
Also you could use the simulator env and run this small test:-
String s1 = "Test";
String s2 = "Test";
if (s1.equals(s2))
System.out.println("Equal");
else
System.out.println("Not Equal");

Related

BreakIterator behaving differently in Android API 29 and API 30

I have made the below function to break String into Hindi Chars. But It behaves differently android API 29 and API 30. In Android 29 Hindi word चक्की is broken into च क् की But in Android 30 it is correctly broken into च क्की.
public List<String> breakIntoHindiChar(String textAnswer) {
List<String> ansCharList = new ArrayList<String>();
Locale hindi = new Locale("hi", "IN");
BreakIterator breaker = BreakIterator.getCharacterInstance(hindi);
breaker.setText(textAnswer);
int start = breaker.first();
for (int end = breaker.next();
end != BreakIterator.DONE;
start = end, end = breaker.next()) {
ansCharList.add(textAnswer.substring(start, end));
}
return ansCharList;
}
How can I solve this problem?
Android implementation of BreakIterator was not able to interpret Bharatiya scripts (abiguda type) accurately. See this bug - https://code.google.com/p/android/issues/detail?id=230832
Looks like it has been fixed for API 30 and has not been backported for previous versions.
How can I solve this problem?
As you noted in your question, the behavior in Android 29 is incorrect, and the behavior in Android 30 is correct.
So it depends on what you think the problem is:
If you think that it is that they fixed the behavior in Android 30 at that has "broken" your app on later versions, then a solution would be for you to copy the Android 29 BreakIterator into your app (with a different name) and use it.
If you think that it is that they didn't fix the behavior in Android 29 (and earlier) and you want your app to behave correctly on older Android, then a solution would be for you to copy the Android 30 BreakIterator into your app (with a different name) and use it.
Note that what you would be doing is to implement either forwards or backwards compatible behavior for BreakIterator, albeit not in the standard class.
(There may be a more elegant solution, but I don't have my own local copy of the Android master repo to dig through the history.)
Note: If you can't replace all of your app's use of BreakIterator with an alternative version of the class, then (AFAIK) there won't be a way to make the behavior consistent. You cannot patch the Android platform. And note that while you could use android.icu.text.BreakIterator instead of android.text.BreakIterator in your own code, that wouldn't fix the problem for other code that your app depends on.
But if you think the problem is that BreakIterator behaves differently in Android 29 vs 30 ... there is no solution to that. It is a fact. Yes ... it does behave differently ... and even Google can't make it not behave differently in Android 30 ... now.

Java difference in String.replace between java 12 and 13

I noticed a difference in the behaviour of the String.replace(CharSequence, CharSequence) between java 12 and 13.
java 12 and earlier:
System.out.println("String"=="String".replace("g","g")); //false
java 13 and later:
System.out.println("String"=="String".replace("g","g")); //true
Found that this is probably due to:
Optimize String.replace(CharSequence, CharSequence) for common cases
Is this unexpected behaviour?
Yes, I'm aware of the equals method.
The API specification makes no guarantees of whether String.replace returns a new String object or if it reuses the original when possible. The result of the comparison is "unspecified". That means it may change from one version to the next, just like you've discovered.
Use the .equals method to compare strings for equality.

Bugs found by FindBugs plugin Eclipse

using bug finder plugin, I found this bugs but does not understand why it was seen as bug in the code. Does anybody know and give me proper explanation regarding these? Thanks.
Source Code - https://drive.google.com/open?id=1gAyHFcdHBShV-9oC5G7GeOtCGf7bXoso;
Patient.java:17 Patient.generatePriority() uses the nextDouble method of Random to generate a random integer; using nextInt is more efficient [Of Concern(18), Normal confidence]
public int generatePriority(){
Random random = new Random();
int n = 5;
return (int)(random.nextDouble()*n);
}
ExaminationRoom.java:25 ExaminationRoom defines equals and uses Object.hashCode() [Of Concern(16), Normal confidence]
public boolean equals(ExaminationRoom room){
if (this.getWaitingPatients().size() == room.getWaitingPatients().size()){
return true;
}
else {
return false;
}
}
ExaminationRoom.java:15 ExaminationRoom defines compareTo(ExaminationRoom) and uses Object.equals() [Of Concern(16), Normal confidence]
// Compares sizes of waiting lists
#Override
public int compareTo(ExaminationRoom o) {
if (this.getWaitingPatients().size() > o.getWaitingPatients().size()){
return 1;
}
else if (this.getWaitingPatients().size() < o.getWaitingPatients().size()){
return -1;
}
return 0;
}
Hospital.java:41 Bad month value of 12 passed to new java.util.GregorianCalendar(int, int, int) in Hospital.initializeHospital() [Scary(7), Normal confidence]
doctors.add(new Doctor("Hermione", "Granger", new GregorianCalendar(1988, 12, 10), Specialty.PSY, room102));
Person.java:29 Return value of String.toLowerCase() ignored in Person.getFullName() [Scariest(3), High confidence]
public String getFullName(){
firstName.toLowerCase();
Character.toUpperCase(firstName.charAt(0));
lastName.toLowerCase();
Character.toUpperCase(lastName.charAt(0));
return firstName + " " + lastName;
}
Don’t create a new Random object each time.
Use random.nextInt(n).
Define a hashCode method in ExaminationRoom.
Having your compareTo method be inconsistent with equals() may or may not be OK.
Use LocalDate instead of GregorianCalendar.
Pick up and use the return values from String.toLowerCase() and Character.toUpperCase().
Consider SpotBugs as a newer alternative to FindBugs.
Details
Random
Creating a new Random object each time you need one gives your poorer pseudo-random numbers with a high risk of numbers being repeated. Declare a static variable holding the Random object outside your method and initialize it in the declaration (Random is thread-safe, so you can safely do that). For drawing a pseudo-random number from 0 through 4 use
int n = 5;
return random.nextInt(n);
It’s not only more efficient (as FindBugs says), I first of all find it much more readable.
hashCode()
#Override
public int hashCode() {
return Objects.hash(getWaitingPatients());
}
compareTo()
The equals method you have shown us seems to contradict FindBugs here. It does look a bit funny, though. Are two waiting rooms considered the same if they have the same number of waiting patients? Please think again. If you end up deciding that they are not equal but should be sorted into the same spot without discrimination, your compareTo method is inconsistent with equals(). If so, please insert a comment stating this fact. If you want FindBugs not to report this as a bug in subsequent analyses, you have two options:
Insert an annotation telling FindBugs to ignore the “bug”.
Create a FindBugs ignore XML file including this point.
I’m sorry I don’t remember the detauls of each, but your search engine should be helpful.
Don’t use GregorianCalendar
The GregorianCalendar class is poorly designed and long outdated. I suggest you evict it from your code and use LocalDate from java.time, the modern Java date and time API, instead.
doctors.add(new Doctor("Hermione", "Granger", LocalDate.of(1988, Month.DECEMBER, 10), Specialty.PSY, room102));
String.toLowerCase()
This was already treated in the other answer. Changing a name to have the first letter in upper case and the rest in lower case is not as simple as it sounds.
firstName.toLowerCase();
Character.toUpperCase(firstName.charAt(0));
The first of these two lines doesn’t modify the string firstName because strings have been designed to be immutable and toLowerCase() to return a new string with all letters in lower case (according to the rules of the default locale of the JVM, confusing). The second line also doesn’t modify any character because Java is call-by-value (look it up), so no method can modify a variable passed as argument. You’re not even passing a variable, but the return value from a different method. Also Character.toUpperCase() returns a new char in lower case.
What you need to do is pick up the values returned from these two method calls, use a substring operation for removing the first letter from the lower-case version of the name and concatenate the upper-case version of that letter with the remainder of the lower-case string. If it’s complicated, I am sure that your search engine can find examples of where and how it is done.
A bit of an aside: You may want to think twice before forcing Dr. Jack McNeil to be written as Mcneil and Dr. Ludwig von Saulsbourg as Von saulsbourg.
SpotBugs
It’s only something I have heard, I haven’t checked myself. The source code of FindBugs has been taken over by a project called SpotBugs. They say that SpotBugs is being developed more actively than FindBugs. So you may consider switching. I am myself a happy SpotBugs user in my daily work.
Links
What issues should be considered when overriding equals and hashCode in Java?
Documentation of Comparable explaining what it means that compareTo() is inconsistent with equals() and how to go about it.
Oracle tutorial: Date Time explaining how to use java.time.
Chicago Hope listing Jack McNeil as Orthopedic Surgeon.
Dr. Jack mentioning Dr. Ludwig von Saulsbourg on the cast.
SpotBugs
First thing to remember about "bug finder" tools, is that they are usually only guidelines. With that said:
The class GregorianCalendar counts months from 0, meaning 0 is January, 11 is December. 12 represents the 13th month which doesn't exist. Since the function expects an int, and you gave it an int, no compiler error is generated, even though this is certainly a bug. This article does a good job explaining the reasons to upgrade, and give examples of how to use the new APIs: https://www.baeldung.com/java-8-date-time-intro
If in doubt, you can always check the documentation. In this case, the class Calendar (which GregorianCalendar extends) delcares a static constant public static final int JANUARY = 0; This confirms that january is indeed 0, but also indicates that we can use this constant in our code. You might find new GregorianCalendar(1988, Calendar.JANUARY, 10) to be a bit more readable.
You may also want to consider switching to the more modern and standard systems used to deal with time. The Java 8 Time libraries are the "new standard", and are definitely worth looking into.
Secondly, Strings are immutable in Java. This means that once a String is created, its value can never be changed. This may be counter to your intuitions, as you may have seen code such as:
String s = "hello";
s = s + " world";
However, this doesn't modify the string s. Instead, s + " world" creates a new String, and assigns it to the variable s.
Similarly, s.toLowerCase() doesn't change what s is, it only generates a new String which you must assign.
You probably want firstName = firstName.toLowerCase();
With your first example, nothing immediately jumps out to me as "bad", but if you look at the messages generated by your tool, they label the first example as "Of Concern", but label the others (e.g. the string.toLowerCase() example) as "Scary"/"Scariest". Although I am not familiar with this tool in particular, I imagine this is indicating more of a "Code Smell", rather than an actual bug.
Perhaps look into Unit Testing, if you want to reassure yourself that your code works.

Investigating a Java bug regarding String.valueOf(float)

In Java is it a possibility that String.valueOf(float) would format a float number differently based on what operating system the code is run on, the version of java and/or the operating systems locale.
For example, with the float number 4.5 would it ever be formatted to "4,5" instead of "4.5"?
String.valueOf(float) calls Float.toString().
Float.toString() calls intern sun.misc.FloatingDecimal.toJavaFormatString(float)
The result string will never contain the sign , bacause of hard-coded '.' (ASCII: 46) inside the BinaryToASCIIBuffer.getChars(chars[])
You can see it if you decompile sun.misc.FloatingDecimal class (in my case java 8 jdk) or see the (similar) implementation in openjdk.

Where can I find the API difference between java5 and java6

I'm working on an Android project and support minSdkVersion=7. When sdk version below 8, it will use java5. And I found String.isEmpty() doesn't exit in java5. So it caused many crashes.
To fix crashes , I have changed String.isEmpty() to String.length() == 0. After that I searched google and didn't find a convenient document figured out the API difference between java5 and java6.
So my question is where can I find such document?
AFAIK, there is no document that exhaustively lists all of the differences between different Java versions at the API level. However, in the (Oracle Java) javadocs, new classes and classes typically have a "Since" tag that says when they were added.
For example, the javadocs for String.isEmpty() say:
public boolean isEmpty()
Returns true if, and only if, length() is 0.
Returns:
true if length() is 0, otherwise false
Since:
1.6

Categories