equals and hashCode: Is Objects.hash method broken? - java

I am using Java 7, and I have the following class below. I implemented equals and hashCode correctly, but the problem is that equals returns false in the main method below yet hashCode returns the same hash code for both objects. Can I get more sets of eyes to look at this class to see if I'm doing anything wrong here?
UPDATE: I replaced the line on which I call the Objects.hash method with my own hash function: chamorro.hashCode() + english.hashCode() + notes.hashCode(). It returns a different hash code, which is what hashCode is supposed to do when two objects are different. Is the Objects.hash method broken?
Your help will be greatly appreciated!
import org.apache.commons.lang3.StringEscapeUtils;
public class ChamorroEntry {
private String chamorro, english, notes;
public ChamorroEntry(String chamorro, String english, String notes) {
this.chamorro = StringEscapeUtils.unescapeHtml4(chamorro.trim());
this.english = StringEscapeUtils.unescapeHtml4(english.trim());
this.notes = notes.trim();
}
#Override
public boolean equals(Object object) {
if (!(object instanceof ChamorroEntry)) {
return false;
}
if (this == object) {
return true;
}
ChamorroEntry entry = (ChamorroEntry) object;
return chamorro.equals(entry.chamorro) && english.equals(entry.english)
&& notes.equals(entry.notes);
}
#Override
public int hashCode() {
return java.util.Objects.hash(chamorro, english, notes);
}
public static void main(String... args) {
ChamorroEntry entry1 = new ChamorroEntry("Åguigan", "Second island south of Saipan. Åguihan.", "");
ChamorroEntry entry2 = new ChamorroEntry("Åguihan", "Second island south of Saipan. Åguigan.", "");
System.err.println(entry1.equals(entry2)); // returns false
System.err.println(entry1.hashCode() + "\n" + entry2.hashCode()); // returns same hash code!
}
}

Actually, you happened to trigger pure coincidence. :)
Objects.hash happens to be implemented by successively adding the hash code of each given object and then multiplying the result by 31, while String.hashCode does the same with each of its characters. By coincidence, the differences in the "English" strings you used occur at exactly one offset more from the end of the string as the same difference in the "Chamorro" string, so everything cancels out perfectly. Congratulations!
Try with other strings, and you'll probably find that it works as expected. As others have already pointed out, this effect is not actually wrong, strictly speaking, since hash codes may correctly collide even if the objects they represent are unequal. If anything, it might be worthwhile trying to find a more efficient hash, but I hardly think it should be necessary in realistic situations.

There is no requirement that unequal objects must have different hashCodes. Equal objects are expected to have equal hashCodes, but hash collisions are not forbidden. return 1; would be a perfectly legal implementation of hashCode, if not very useful.
There are only 32 bits worth of possible hash codes, and an unbounded number of possible objects, after all :) Collisions will happen sometimes.

HashCode being 32 bit int value, there is always a possibility of collisions(same hash code for two objects), but its rare/coincidental. Your example is one of the such a highly coincidental one. Here is the explanation.
When you call Objects.hash, it internally calls Arrays.hashCode() with logic as below:
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
For your 3 param hashCode, it results into below:
31 * (31 * (31 *1 +hashOfString1)+hashOfString2) + hashOfString3
For your first object. Hash value of individual Strings are:
chamorro --> 1140493257
english --> 1698758127
notes --> 0
And for second object:
chamorro --> 1140494218
english --> 1698728336
notes -->0
If you notice, first two values of the hash code in both objects are different.
But when it computes the final hash code as:
int hashCode1 = 31*(31*(31+1140493257) + 1698758127)+0;
int hashCode2 = 31*(31*(31+1140494218) + 1698728336)+0;
Coincidentally it results into same hash code 1919283673 because int is stored in 32 bits.
Verify the theory your self be using the code segment below:
public static void main(String... args) {
ChamorroEntry entry1 = new ChamorroEntry("Åguigan",
"Second island south of Saipan. Åguihan.", "");
ChamorroEntry entry2 = new ChamorroEntry("Åguihan",
"Second island south of Saipan. Åguigan.", "");
System.out.println(entry1.equals(entry2)); // returns false
System.out.println("Åguigan".hashCode());
System.out.println("Åguihan".hashCode());
System.out.println("Second island south of Saipan. Åguihan.".hashCode());
System.out.println("Second island south of Saipan. Åguigan.".hashCode());
System.out.println("".hashCode());
System.out.println("".hashCode());
int hashCode1 = 31*(31*(31+1140493257) + 1698758127)+0;
int hashCode2 = 31*(31*(31+1140494218) + 1698728336)+0;
System.out.println(entry1.hashCode() + "\n" + entry2.hashCode());
System.out.println(getHashCode(
new String[]{entry1.chamorro, entry1.english, entry1.notes})
+ "\n" + getHashCode(
new String[]{entry2.chamorro, entry2.english, entry2.notes}));
System.out.println(hashCode1 + "\n" + hashCode2); // returns same hash code!
}
public static int getHashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
If you use some different string parameters, hope it will result into different hashCode.

it's not necessary for two unequal objects to have different hashes, the important thing is to have the same hash for two equal objects.
I can implement hashCode() like this :
public int hashCode() {
return 5;
}
and it will stay correct (but inefficient).

Related

How to sort sequential decimal values in java

Basically, I have a set of BigDecimal values for example
[3.2,3.10,3.12,3.17,3.9].
I want to sort them based on the values after the dot using java.
The expected output should be like [3.2,3.9,3.10,3.12,3.17].
how do I write a code for this can you please help?
It seems you actually want to process version numbers, so I strongly discourage from abusing BigDecimal for that purpose. It’s too easy to accidentally confuse your logic with BigDecimal’s original numeric semantics.
Create your own class reflecting the actual purpose, e.g.
public record Version(int major, int minor) implements Comparable<Version> {
public static Version parse(String s) {
int dot = s.indexOf('.');
return dot < 0? new Version(Integer.parseInt(s), 0):
new Version(Integer.parseInt(s, 0, dot, 10),
Integer.parseInt(s, dot + 1, s.length(), 10));
}
#Override
public int compareTo(Version v) {
return major != v.major?
Integer.compare(major, v.major): Integer.compare(minor, v.minor);
}
#Override
public String toString() {
return major + "." + minor;
}
}
With such a class, you’ll never run into the problem of contradicting semantics.
When you use it with ,e.g.
Stream.of("3.2","3.10","3.12","3.17","3.9").map(Version::parse)
.sorted().forEachOrdered(System.out::println);
It will print
3.2
3.9
3.10
3.12
3.17
Create a method that returns the decimal part according to the scale of the given number
(e.g. calling it with 3.10 will return 10):
private static BigDecimal decimalPart(BigDecimal num) {
var dec = num.remainder(BigDecimal.ONE); // decimal part (e.g. 0.10)
var adj = dec.movePointRight(dec.scale());
return adj;
}
then use that to create a Comparator used for sorting:
var byDecimalPart = Comparator.comparing(SortedDecimal::decimalPart);
var sorted = input
.stream()
.sorted(byDecimalPart)
.toList(); // or .collect(...) or .forEach(...)
probably not correct for negative numbers (missing specification)

How to effectively remove updated HashSet items

Given the following snippet, how do we effectively remove elements on which have been previously updated/changed?
public static class Foo {
#Override
public int hashCode() {
return new Random().nextInt();
}
}
public static void main(String[] args) {
Set<Foo> set = new HashSet<>();
set.add(new Foo());
set.removeIf(f -> true); // Returns true, but no deletion occurs
assert set.size() == 0; // Fails as set still contains it's single item
}
Note: The above snippet is intended to simulate a different Foo upon next call to Object::hashCode (on Set::remove and Set::removeIf).
EDIT:
For those who did not understand the "random hash" part, here is a different view of the problem stated above:
public static class Bar {
public String firstName;
public String lastName;
public Bar() {
this(null, null);
}
public Bar(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public int hashCode() {
int result = 1;
result *= 59 + (firstName == null ? 43 : firstName.hashCode());
result *= 59 + (lastName == null ? 43 : lastName.hashCode());
return result;
}
}
public static void main(String[] args) {
Set<Bar> set = new HashSet<>();
String originalFirstName = "FOO";
String updatedFirstName = "FOO_CHANGED";
// Create bar
Bar bar = new Bar();
bar.firstName = originalFirstName;
bar.lastName = "BAR";
// Add bar
set.add(bar);
// Change bar
System.out.println("Bar hash (now): " + bar.hashCode());
bar.firstName = updatedFirstName;
System.out.println("Bar hash (new): " + bar.hashCode());
Bar oldBar = new Bar(originalFirstName, bar.lastName);
Bar changedBar = new Bar(bar.firstName, bar.lastName);
System.out.println("Old bar hash: " + oldBar.hashCode()); // Hash matches old value
System.out.println("Changed bar hash: " + changedBar.hashCode()); // Hash matches new value
set.remove(oldBar); // Removes no elements (returns false)
set.remove(changedBar); // Removes no elements (returns false)
set.removeIf(f -> true); // Removes no elements (returns true)
Iterator<Bar> iterator = set.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove(); // Fails silently
}
assert set.size() == 0;
}
There's no random hash at all.
There are different hashes indeed, but apparently the elements can never be removed if they have ever been changed (therefore, their hash), regardless what. We can confirm that on both Set::remove calls, where set.remove(oldBar) should have removed the element as oldBar hash equals to the hash when bar was added.
As all other answers and comments, firstly, I should say that hashCode should remain consistent: it supposed remain the same when an element is stored in a hash based collection.
Amusingly, the code snippet in OpenJDK 11 will return 0 when the set's size is queried, but on OpenJDK 8 it will remain 1.
This happened due to changes in the standard library (see JDK-8170733):
The removeIf in HashMap#keySet (HashSet uses a HashMap underneath) is not overridden, so it relies on Iterator#remove.
Implementation of the latter method has been changed to avoid recomputing the hashCode inside the HashMap.HashIterator::remove method.
So the removeIf will successfully remove elements
See this commit:
- K key = p.key;
- removeNode(hash(key), key, null, false, false);
+ removeNode(p.hash, p.key, null, false, false);
Once again: do not rely rely on this implementation detail
The problem here is that if you modify the object in such a way that the hashcode is different then it is no longer structurally the same object. Another way to say this, original.equals(modified) is false (or at least should be due to the contracts of equals() and hashCode(). One solution is to modify hashCode() to calculate based on some invariant. In other words, the returned hashcode is only based on the identifying data in a Foo object that will never change no matter what. For example, this could be an id, such as for an object that maps to an underlying database table.
Alternatively, you could find a different data structure that matches your use case better. For example an ArrayList might be more appropriate since you can remove items at a given index regardles of the state of that object.
Not consistent hashCode is wrong.
However I could not understand how it should affect you in your case as you invoke removeIf which iterates all elements of the set.
So I tried it using JAVA 11 and it worked. Set was emptied and size returned was 0 as expected. I am curious of what configurations you use.
public static void main(String[] args){
Set<Foo> s = new HashSet<>();
s.add(new Foo("user1", 3));
s.add(new Foo("user2", 5));
s.forEach( e -> System.out.println(e));
s.removeIf(f-> true);
s.forEach( e ->System.out.println(e));
System.out.println(s.size());
}

ArrayList does not print removed objects

I have a class called Polynomial with an ArrayList made up of term objects, there is an external file that is read by a Scanner object in my test class. The Scanner reads the line for 4 different key words and acts accordingly. ex. INSERT 3 2. Would call my insert method and print out 3x^2. Now I have a delete method with two parameters. When I call the method in the test class nothing happens, the same thing gets printed and nothing has been removed. Am I missing something or doing it wrong all together? Any help is greatly appreciated.
public void delete (int coeff, int expo)
{
for (int i = 0; i<terms.size(); i++)
{
Term current = terms.get(i);
terms.remove(current.getCoeff());
terms.remove(current.getExpo());
}
}
I also have a Term class that creates a term object, and has two methods to get the coefficient and exponent.
Here is a snippet of my test class:
public static void main(String[] args) throws IOException
{
// TODO code application logic here
Polynomial polyList = new Polynomial();
Scanner inFile = new Scanner(new File("operations2.txt"));
while(inFile.hasNext())
{
Scanner inLine = new Scanner(inFile.nextLine());
String insert = inLine.next();
if(insert.equals("INSERT"))
{
int coeff = inLine.nextInt();
int expo = inLine.nextInt();
polyList.insert(coeff, expo);
}
if(insert.equals("DELETE"))
{
int coeff = inLine.nextInt();
int expo = inLine.nextInt();
polyList.delete(coeff, expo);
}
}
System.out.println(polyList.toString());
}
}
Edit: this is a sample of the .txt file that is being read by the scanner class:
INSERT 3 2
INSERT 4 4
INSERT 1 6
INSERT 2 0
INSERT 5 2
INSERT 6 3
PRODUCT
DELETE 3 2
INSERT 2 7
DELETE 4 4
INSERT 4 10
Edit: Here is the Term class:
class Term
{
//instance vars
private int coefficient;
private int exponent;
public Term(int coeff, int expo)
{
coefficient = coeff;
exponent = expo;
}
public int getCoeff()
{
return coefficient;
}
public int getExpo()
{
return exponent;
}
#Override
public int hashCode()
{
return coefficient + exponent;
}
#Override
public boolean equals(Object o)
{
if (!(o instanceof Term))
{
return false;
}
Term t = (Term)o;
return coefficient == t.coefficient && exponent == t.exponent;
}
}
If your delete() method is trying to delete the Twrm with the specified coefficients, I recommend the following:
Override the equals() method to return true if the argument is a Term with the same coefficient and exponential
Override the hashCode() method to return a hash based on the same two values
Since the equals() method should make a value comparison, such an implementation is quite reasonable.
Once you've done that, your delete method becomes one line:
terms.remove(new Term(coeff, expo));
The implementation should look like this:
// in the Term class
#Override
public boolean equals(Object o) {
if (!(o instanceof Term)
return false;
Term t = (Term)o;
return coeff == t.coeff && expo == t.expo;
}
Although overriding the hashCode method is not strictly required to make your code work, it is good practice, so here's an example impl:
#Override
public int hashCode() {
return 31 * coeff + expo;
}
You're not trying to remove the Term from the terms list, but rather trying to remove the coefficient and exponent.
for (int i = 0; i<terms.size(); i++)
{
Term current = terms.get(i); // Your list contains Term objects
terms.remove(current.getCoeff()); // but you are try to removing a coefficient
terms.remove(current.getExpo()); // and an exponent
}
Just a general note also that removing this way will not work because i will be getting larger and your list will be getting smaller. So by the time you get to remove the last term for example (where i = terms.size() - 1), there will only be 1 item left in the list. If you're trying to remove all of the items, consider the list's clear method.
Why does your delete method take the arguments coeff and expo ....
...it does not do anything with them.
In fact, the delete method looks very suspicious. You will need to give more detail on what the terms array looks like, right now it makes no sense.
rolfl

Java MultiMap Not Recognizing Key

I'm trying to store multiple values for a key in a data structure so I'm using Guava (Google Collection)'s MultiMap.
Multimap<double[], double[]> destinations = HashMultimap.create();
destinations = ArrayListMultimap.create();
double[] startingPoint = new double[] {1.0, 2.0};
double[] end = new double[] {3.0, 4.0};
destinations.put(startingPoint, end);
System.out.println(destinations.containsKey(startingPoint));
and it returns false.
Note: Key-values are being stored in the multimap as the destinations.size() increases when I put something there.It also does not happen when keys are String instead of double[].
Any idea what the problem is?
Edit: Many thanks to Jon Skeet I now implemented the class:
class Point {
double lat;
double lng;
public boolean equals(Point p) {
if (lat == p.lat && lng == p.lng)
return true;
else
return false;
}
#Override
public int hashCode() {
int hash = 29;
hash = hash*41 + (int)(lat * 100000);
hash = hash*41 + (int)(lng * 100000);
return hash;
}
public Point(double newlat, double newlng) {
lat = newlat;
lng = newlng;
}
}
And now I have a new problem. This is how I'm using it:
Multimap<Point, Point> destinations = HashMultimap.create();
destinations = ArrayListMultimap.create();
Point startingPoint = new Point(1.0, 2.0);
Point end = new Point(3.0, 4.0);
destinations.put(startingPoint, end);
System.out.println( destinations.containsKey(startingPoint) );
System.out.println( destinations.containsKey(new Point(1.0, 2.0)) );
The first one returns true, the second one returns false. It gives me an error if I put #Override before the equals method.Any Idea what the problem is now?
Thanks :)
Edit2: It now behaves exactly as expected when I changed equals to this:
#Override
public boolean equals(Object p) {
if (this == p)
return true;
else if ( !(p instanceof Point) )
return false;
else {
Point that = (Point) p;
return (that.lat == lat) && (that.lng == lng);
}
}
Thanks everyone.
You're using arrays as the hash keys. That's not going to work - Java doesn't override hashCode and equals for arrays. (The Arrays class provides methods to do this, but it's not going to help you here.) Admittedly I'd expect it to work in this specific case, where you're using the exact same reference for both put and containsKey... When I test your code, it prints true. Are you sure you can reproduce it with exactly your code?
For example, while I'd expect it to work for the code you've given, I wouldn't expect this to work:
// Logically equal array, but distinct objects
double[] key = (double[]) startingPoint.clone();
System.out.println(destinations.containsKey(key));
It sounds like you shouldn't really be using double[] here - you should create a Point class which has two double variables, and overrides equals and hashCode.
Additionally, using double values in hash keys is usually a bad idea anyway, due to the nature of binary floating point arithmetic. That's going to be a problem even using the Point idea above... it should be okay if you don't need to actually do any arithmetic (if you're just copying values around) but take great care...
The problem is that you cannot hash "equal" arrays and get the same result each time. For example:
public static void main(String[] args) {
System.out.println(new double[]{1.0, 2.0}.hashCode());
System.out.println(new double[]{1.0, 2.0}.hashCode());
}
will result something like
306344348
1211154977

How to sort a String collection that contains numbers?

I have a String Vector that contains data like this :
5:34, 5:38, 17:21, 22:11, ...
If i try to merge this using Collections.sort( ... ); it will appear like this :
17:21, 22:11, 5:34, 5:38
Actually i want it to appear like this :
5:34, 5:38, 17:21, 22:11
So i want to sort the elements according to the number before the colon ":" then if some elements have the same number before ":" then sort them according to the number after the ":".
What is the simplest way to do this ?
The correct way to do this is to not store non-string values as strings.
The data in your collection has some structure and rules and can't be any arbitrary string. Therefore you should not use the String data type.
Let's define a type called TwoNumbers (because I don't know what the type should represent, even if I could guess):
class TwoNumbers implements Comparable<TwoNumbers> {
private final int num1;
private final int num2;
public TwoNumbers(int num1, int num2) {
if (num1 <= 0 || num2 <= 0) {
throw new IllegalArgumentException("Numbers must be positive!");
}
this.num1 = num1;
this.num2 = num2;
}
public static TwoNumbers parse(String s) {
String[] parts = s.split(":");
if (parts.length != 2) {
throw new IllegalArgumentException("String format must be '<num>:<num>'");
}
try {
return new TwoNumbers(Integer.parseInt(parts[0]), Integer.parseInt(parts[0]));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("parts must be numeric!", e);
}
}
public int getNum1() {
return num1;
}
public int getNum2() {
return num2;
}
#Override
public int compareTo(TwoNumbers o) {
if (o == null) {
return 1;
}
int diff = Integer.compare(o.num1, this.num1);
if (diff == 0) {
diff = Integer.compare(o.num2, this.num2);
}
return diff;
}
}
The compareTo method exists as the implementation of the Comparable interface: it defines how objects of this type are ordered.
I've used the final fields (and don't provide setters), because the class implements immutable objects.
This way you can directly sort your data without an additional Comparator and don't need to distribute all that "split and parse" code all over your program. Instead you have a single class that's responsible for handling that specific format and all the other pieces of code can just use that.
This is horribly inefficient, but it should do the job.
Collections.sort(data, new Comparator<String>(){
public int compare(String a, String b){
String[] as = a.split(":");
String[] bs = b.split(":");
int result = Integer.valueOf(as[0]).compareTo(Integer.valueOf(bs[0]));
if(result==0)
result = Integer.valueOf(as[1]).compareTo(Integer.valueOf(bs[1]));
return result;
}
})
(Hint: if it were my code, I'd optimize it to use substrings instead of String.split(), but I'm too lazy)
You could either create a custom Comparator to split the String and parse it into two ints, or create a bespoke class to represent each String and store that in the Collection instead. I favour the latter approach as you only incur the overhead of splitting / parsing the String once; e.g.
public class Data implements Comparable<Data> {
private final int prefix;
private final int suffix;
public Data(String str) {
String[] arr = str.split(":");
if (arr.length != 2) {
throw new IllegalArgumentException();
}
this.prefix = Integer.parseInt(arr[0]);
this.suffix = Integer.parseInt(arr[1]);
}
public int compareTo(Data data) {
// Should really avoid subtraction in case of overflow but done to keep code brief.
int ret = this.prefix - data.prefix;
if (ret == 0) {
ret = this.suffix - data.suffix;
}
return ret;
}
// TODO: Implement equals and hashCode (equals to be consistent with compareTo).
public String toString() { return String.format("%d:%d", prefix, suffix); }
}
Then it's simply a case of storing some Data objects in your Collection; e.g.
List<Data> l = new ArrayList<Data>();
l.add(new Data("13:56"));
l.add(new Data("100:16"));
l.add(new Data("9:1"));
Collections.sort(l);
One more thing - You mention you're using a Vector. You should try to avoid using Vector / Hashtable as these have been superseded by List / Map, which were introduced as part of the Collections Framework in JDK 1.2.
Create a java.util.Comparator and provide it to the sort method.
Implement your own Comparator class that compares two values and call Collections.sort(List list, Comparator c).
Implement your own Comparator and give it as second argument to the Colelctions.sort method.
Generally, objects in Java (including Collections) are compared with their default hashCode() and equals() method. For the built in objects and data types (like String, Integet etc.,) the hashCode() is computed internally and hence they are used as guaranteed by the JLS (Java Language Specification).
As we can't always be dependent upon the default/built in objects and we need to deal with our own custom objects (like Employee, Customer etc.,), we should have to override hashCode() and equals() method, so that we can provide the true/false according to the "BEST" equality of the objects of our custom classes.
Similary, sort() involves a comparison act that indeed needs a Comparator (which is a class implementing the Comparator interface with an overridden method of compare method). You should also override the compare method that takes two Objects to be compared and returns a result (0 for equal, 1 for the 1st object being greater than the second, 2 for the reverse of case 1).
Now, you data should be dealt in a different way which is quite away from the normal comparsion. You need to split the data into two parts (using a split method you can do) and then you can do the individual comparison on the two parats (first part before the colon, second part after the colon).
Finally, you should provide an instance of this custom comparator to the sort method, that will eventually do the custom sorting for your custom data :)
I think this is pretty simple:
public class NumericalStringSort {
public static void main(String[] args) {
List<String> input = Arrays.asList(new String[] {"17:21", "22:11", "5:34", "5:38"});
Collections.sort(input, new NumericalStringComparator());
System.out.println(input);
}
public static class NumericalStringComparator implements Comparator<String> {
public int compare(String object1, String object2) {
return pad(object1).compareTo(pad(object2));
}
private String pad(String input) {
return input.indexOf(":") == 1 ? "0" + input : input;
}
}
}
Just found this (quite old) post and the answers didn't quite solve the problem I have. I needed a more generic solution, as the values were user inputs and something like "abc 1 a 12" and "abc 1 a 1" should be sorted in order of the contained number(s). So I wrote the following Comparator:
new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
String[] s1=splitNumeric(o1);
String[] s2=splitNumeric(o2);
for (int x=0;x<s1.length&&x<s2.length;x++){
if (!s1[x].equals(s2[x])){
if (s1[x].charAt(0)=='N' && s2[x].charAt(0)=='N'){
long l1=Long.parseLong(s1[x].substring(1));
long l2=Long.parseLong(s2[x].substring(1));
return (int)Math.signum(l1-l2);
}
break;
}
}
return o1.compareTo(o2);
}
}
While the function splitNumeric is defined as follows:
private String[] splitNumeric(String s){
final String numbers="0123456789";
LinkedList<String> out=new LinkedList<String>();
int state=-1;
for (int x=0;x<s.length();x++){
if (numbers.contains(s.charAt(x)+"")){
if (state==1)
out.set(out.size()-1,out.getLast()+s.charAt(x));
else{
state=1;
out.add("N"+s.charAt(x));
}
}
else{
if (state==0)
out.set(out.size()-1,out.getLast()+s.charAt(x));
else{
state=0;
out.add("S"+s.charAt(x)+"");
}
}
}
return out.toArray(new String[0]);
}
The code will sort Strings
"X 124 B"
"X 1 Y"
"X 111 Z"
"X 12 Y"
"12:15"
"12:13"
"12:1"
"1:1"
"2:2"
as follows:
"1:1"
"2:2"
"12:1"
"12:13"
"12:15"
"X 1 Y"
"X 12 Y"
"X 111 Z"
"X 124 B"
Enjoy :)

Categories