Why does this code throw exception - Comparison method violates its general contract - java

I am using the following code that is throwing a IllegalArgumentException :
// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.
package jodd.util;
import java.util.Comparator;
/**
* Compares two strings in natural, alphabetical, way.
*/
public class NaturalOrderComparator<T> implements Comparator<T> {
protected final boolean ignoreCase;
public NaturalOrderComparator() {
ignoreCase = false;
}
public NaturalOrderComparator(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
}
/**
* Compare digits at certain position in two strings.
* The longest run of digits wins. That aside, the greatest
* value wins.
*/
protected int compareDigits(String str1, int ndx1, String str2, int ndx2) {
int bias = 0;
while (true) {
char char1 = charAt(str1, ndx1);
char char2 = charAt(str2, ndx2);
boolean isDigitChar1 = CharUtil.isDigit(char1);
boolean isDigitChar2 = CharUtil.isDigit(char2);
if (!isDigitChar1 && !isDigitChar2) {
return bias;
}
if (!isDigitChar1) {
return -1;
}
if (!isDigitChar2) {
return 1;
}
if (char1 < char2) {
if (bias == 0) {
bias = -1;
}
} else if (char1 > char2) {
if (bias == 0) {
bias = 1;
}
} else if (char1 == 0 && char2 == 0) {
return bias;
}
ndx1++;
ndx2++;
}
}
public int compare(T o1, T o2) {
String str1 = o1.toString();
String str2 = o2.toString();
int ndx1 = 0, ndx2 = 0;
int zeroCount1, zeroCount2;
char char1, char2;
int result;
while (true) {
// only count the number of zeroes leading the last number compared
zeroCount1 = zeroCount2 = 0;
char1 = charAt(str1, ndx1);
char2 = charAt(str2, ndx2);
// skip over leading spaces or zeros in both strings
while (Character.isSpaceChar(char1) || char1 == '0') {
if (char1 == '0') {
zeroCount1++;
} else {
zeroCount1 = 0; // counts only last 0 prefixes, space char interrupts the array of 0s
}
ndx1++;
char1 = charAt(str1, ndx1);
}
while (Character.isSpaceChar(char2) || char2 == '0') {
if (char2 == '0') {
zeroCount2++;
} else {
zeroCount2 = 0;
}
ndx2++;
char2 = charAt(str2, ndx2);
}
// process digits
boolean isDigitChar1 = CharUtil.isDigit(char1);
boolean isDigitChar2 = CharUtil.isDigit(char2);
if (isDigitChar1 && isDigitChar2) {
result = compareDigits(str1, ndx1, str2, ndx2);
if (result != 0) {
// not equals, return
return result;
}
// equal numbers
if (zeroCount1 != zeroCount2) {
return zeroCount1 - zeroCount2;
}
}
if (char1 == 0 && char2 == 0) {
// the end; the strings are the same, maybe compare ascii?
return zeroCount1 - zeroCount2;
}
// check when one of the numbers is just zeros
if (isDigitChar1 || isDigitChar2) {
if (zeroCount1 != zeroCount2) {
return zeroCount2 - zeroCount1;
}
}
// checks when both numbers are zero
if (zeroCount1 != zeroCount2) {
return zeroCount1 - zeroCount2;
}
// compare chars
if (ignoreCase) {
char1 = Character.toLowerCase(char1);
char2 = Character.toLowerCase(char2);
}
if (char1 < char2) {
return -1;
}
if (char1 > char2) {
return 1;
}
ndx1++;
ndx2++;
}
}
/**
* Safe charAt.
*/
private static char charAt(String s, int i) {
if (i >= s.length()) {
return 0;
}
return s.charAt(i);
}
}
throws the exception:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:747)
at java.util.TimSort.mergeAt(TimSort.java:483)
at java.util.TimSort.mergeCollapse(TimSort.java:410)
at java.util.TimSort.sort(TimSort.java:214)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
This is called by the following function:
#Override
public int compare(final T o1, final T o2) {
int result;
final MyObject obj1 = (MyObject) o1;
final MyObject obj2 = (MyObject) o2;
return result = compareStringId(obj1.getStringId(),obj2.getStringId());
}
private int compareStringId(final String Id1, final String Id2) {
return super.compare((T) Id1, (T) Id2);
}
It work fine on our local machines, however it fails on production and we have no means to connect to that machine to figure out why. Could you please assist

The issue was with the incorrect implementation of a Comparator. According to Java documentation, a Comparator must be both reflexive and transitive. In this case, the transivity was not guaranteed. Prior Java 8 that was not a big issue, i.e. the sorting implementation (MergeSort) would not throw exception. Java8 changed default sorting implementation to TimSort that is much more sensitive to comparators with invalid contract, hence it might throw an exception.
However, this does not help you much in solving your issue. How about to check the latest version of the same class here. It has been upgraded to support the comparator contract, plus it works better on some edge cases, not to mention initial support for accents.

Related

check whether we can split string in two half and both halfves are equal?

I am working on a project where I need to add below method in SampleQueue class - .
public static boolean isValid(String s)
Above method should do this - It will take a String as an input
parameter. Consider strings that can be split so that their first half
is the same as their second half (ignoring blanks, punctuation, and
case). For example, the string "treetree" can be split into "tree" and
"tree". Another example is "world, world". After ignoring blanks and
the comma, the two halves of the string are the same. However, the
string "kattan" has unequal halves, as does the string "abcab".
Basically my method should return true when string has the property above and false otherwise. We need to only use methods in SampleQueue class as shown below to implement the method:
public class SampleQueue<T> {
private T[] queue;
private int frontIndex;
private int backIndex;
private static final int DEFAULT_INITIAL_CAPACITY = 200;
public SampleQueue() {
this(DEFAULT_INITIAL_CAPACITY);
}
public SampleQueue(int initialCapacity) {
T[] tempQueue = (T[]) new Object[initialCapacity + 1];
queue = tempQueue;
frontIndex = 0;
backIndex = initialCapacity;
}
public void enqueue(T newEntry) {
ensureCapacity();
backIndex = (backIndex + 1) % queue.length;
queue[backIndex] = newEntry;
}
public T getFront() {
T front = null;
if (!isEmpty())
front = queue[frontIndex];
return front;
}
public T dequeue() {
// some stuff here
}
private void ensureCapacity() {
// some stuff here
}
public boolean isEmpty() {
// some stuff here
}
public void clear() {
// some stuff here
}
public static boolean isValid(String s) {
if (s == null || s.isEmpty()) {
return false;
}
SampleQueue<Character> myQueue = new SampleQueue<>();
for (char ch : s.trim().toLowerCase().toCharArray()) {
if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
myQueue.enqueue(ch);
}
// all is this right way to check the length?
if (myQueue.queue.length % 2 == 1) {
return false;
}
// now I am confuse here?
}
}
I implemented few things in the isValid method basis on this logic I came up with but I am confuse on what to do for the case length is even?
Enqueue all of the string’s characters—excluding blanks and
punctuation—one at a time. Let the length of the queue be n. If n is
odd, return false. If n is even then what should I do?
This seems overly complicated; use a regular expression to remove everything not a letter and then test if the two halves of the String are equal. Like,
public static boolean isValid(String s) {
String t = s.replaceAll("[^A-Za-z]", "");
return t.substring(0, t.length() / 2).equals(t.substring(t.length() / 2, t.length()));
}

Sorting Version Numbers

My code below is supposed to sort version numbers into proper order. For the most part it works, but it fails a hidden test case that I don't have access to. Given that is there any edge case you can see that I might be missing.
import java.util.*;
public class Answer {
public static void main(String[] args)
{
//Testing
String[] versions = {"0.0.0","0","0.0","1.113","0.0.0.1","2.0.0","1.2","2","0.1","1.2.1","1.1.1","2.0"};
String[] results = answer(versions);
for(int i =0; i<results.length;i++)
{
System.out.println(results[i]);
}
}
public static String[] answer(String[] l) {
String temp = new String();
//Insertion sort on the given array to assign correct version numbers
for (int i = 1; i < l.length; i++) {
for(int j = i ; j > 0 ; j--){
if(compareVersion(l[j],l[j-1])<0){
temp = l[j];
l[j] = l[j-1];
l[j-1] = temp;
}
}
}
return l;
}
//Will compare version numbers breaking it apart into a String array
public static int compareVersion(String version1, String version2) {
String[] arr1 = version1.split("\\.");
String[] arr2 = version2.split("\\.");
int i=0;
while(i<arr1.length || i<arr2.length){
if(i<arr1.length && i<arr2.length){
if(Integer.parseInt(arr1[i]) < Integer.parseInt(arr2[i])){
return -1;
}else if(Integer.parseInt(arr1[i]) > Integer.parseInt(arr2[i])){
return 1;
}
else if(Integer.parseInt(arr1[i]) == Integer.parseInt(arr2[i]))
{
int result = specialCompare(version1,version2);
if(result != 0)
{
return result;
}
}
} else if(i<arr1.length){
if(Integer.parseInt(arr1[i]) != 0){
return 1;
}
} else if(i<arr2.length){
if(Integer.parseInt(arr2[i]) != 0){
return -1;
}
}
i++;
}
return 0;
}
// Meant for when version numbers such as 2 and 2.0 arise. This method will make sure to
// put the smaller version number ( in length) first
public static int specialCompare(String str1, String str2)
{
String[] arr1 = str1.split("\\.");
String[] arr2 = str2.split("\\.");
for(int i =1; i<arr1.length;i++)
{
if(Integer.parseInt(arr1[i]) != 0)
{
return 0;
}
}
for(int j =1; j<arr2.length;j++)
{
if(Integer.parseInt(arr2[j]) != 0)
{
return 0;
}
}
if(arr1.length < arr2.length)
{
return -1;
}
else
{
return 1;
}
}
}
I read Lukas Eder's blog post in the comment above and created a java.util.Comparator which orders by version (or chapter) number, based on a JDK proposal.
VersionNumberComparator is defined in a GitHub gist. The follow code shows how it works.
import java.util.ArrayList;
import java.util.List;
public class JavaTest {
public static void main(String[] args) {
final List<String> chapters = new ArrayList<>();
chapters.add("1.1");
chapters.add("1.2");
chapters.add("1");
chapters.add("1.3");
chapters.add("1.1.1");
chapters.add("5.6");
chapters.add("1.1.10");
chapters.add("4");
chapters.add("1.1.9");
chapters.add("1.2.1.10");
chapters.add("2.1.1.4.5");
chapters.add("1.2.1.9");
chapters.add("1.2.1");
chapters.add("2.2.2");
chapters.add("1.2.1.11");
System.out.println("UNSORTED: " + chapters.toString());
chapters.sort(VersionNumberComparator.getInstance());
System.out.println("SORTED: " + chapters.toString());
}
}
Produces the following output:
UNSORTED: [1.1, 1.2, 1, 1.3, 1.1.1, 5.6, 1.1.10, 4, 1.1.9, 1.2.1.10, 2.1.1.4.5, 1.2.1.9, 1.2.1, 2.2.2, 1.2.1.11]
SORTED: [1, 1.1, 1.1.1, 1.1.9, 1.1.10, 1.2, 1.2.1, 1.2.1.9, 1.2.1.10, 1.2.1.11, 1.3, 2.1.1.4.5, 2.2.2, 4, 5.6]
I have recently had a need to do this in a more generic way for arbitrary file names, similar to how Windows Explorer sorts files:
I wrote a blog post about this. The idea was inspired by this answer here. The comparator used for ordering files this way looks like this:
public final class FilenameComparator implements Comparator<String> {
private static final Pattern NUMBERS =
Pattern.compile("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
#Override
public final int compare(String o1, String o2) {
// Optional "NULLS LAST" semantics:
if (o1 == null || o2 == null)
return o1 == null ? o2 == null ? 0 : -1 : 1;
// Splitting both input strings by the above patterns
String[] split1 = NUMBERS.split(o1);
String[] split2 = NUMBERS.split(o2);
for (int i = 0; i < Math.min(split1.length, split2.length); i++) {
char c1 = split1[i].charAt(0);
char c2 = split2[i].charAt(0);
int cmp = 0;
// If both segments start with a digit, sort them numerically using
// BigInteger to stay safe
if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9')
cmp = new BigInteger(split1[i]).compareTo(new BigInteger(split2[i]));
// If we haven't sorted numerically before, or if numeric sorting yielded
// equality (e.g 007 and 7) then sort lexicographically
if (cmp == 0)
cmp = split1[i].compareTo(split2[i]);
// Abort once some prefix has unequal ordering
if (cmp != 0)
return cmp;
}
// If we reach this, then both strings have equally ordered prefixes, but
// maybe one string is longer than the other (i.e. has more segments)
return split1.length - split2.length;
}
}
If you want to sort some versions (e.g. 0.4.3, 5.3.5, 1.2.4) you could use my approach which includes using a java.util.Comparator. To use this you have to use a sorting method (e.g. Arrays.sort(new String[] {"0.4.3", "5.3.5", "1.2.4"}, new VersionComparator())). The VersionComparator class is written below:
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public final class VersionComparator implements Comparator<String> {
#Override
public int compare(String version1, String version2) {
if (checkVersionFormat(version1) == false || checkVersionFormat(version2) == false) {
System.out.println("One of the given versions uses a false format");
}
List<String> firstVersionElements = Arrays.asList(version1.split("\\."));
List<String> secondVersionElements = Arrays.asList(version2.split("\\."));
int maxVersionElements = getMaxNumber(firstVersionElements.size(), secondVersionElements.size(), 0);
for (int counter = 0; counter < maxVersionElements; counter++) {
if (firstVersionElements.size() == counter && secondVersionElements.size() == counter) {
return 0;
}
if (firstVersionElements.size() == counter) {
return 1;
}
if (secondVersionElements.size() == counter) {
return -1;
}
int firstIntElement = Integer.valueOf(firstVersionElements.get(counter));
int secondIntElement = Integer.valueOf(secondVersionElements.get(counter));
if (firstIntElement < secondIntElement) {
return 1;
}
if (firstIntElement > secondIntElement) {
return -1;
}
}
return 0;
}
private boolean checkVersionFormat(String version) {
String versionCopy = new String(version);
String[] validChars = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "." };
for (String character : validChars) {
versionCopy = versionCopy.replace(character, "");
}
return versionCopy.equals("");
}
public static int getMaxNumber(Integer... ints) {
int maximumInt = ints[0];
for (int processInt : ints) {
if (processInt > maximumInt) {
maximumInt = processInt;
}
}
return maximumInt;
}
}
From your comments in your code above the specialCompare method…
Meant for when version numbers such as 2 and 2.0 arise. This method
will make sure to put the smaller version number (in length) first
So I am guessing from this that you want the version to be sorted by the length of the version number. Example: from your supplied versions string array you want to sort as below…
0
2
0.0
0.1
1.2
1.113
2.0
0.0.0
1.1.1
1.2.1
2.0.0
0.0.0.1
If this is the case, then you seem to be making this more complicated than it has to be. When you get two versions where the version lengths differ, then the one with the shorter version length should go first. So a simple check on the length of the version split arrays should solve this. If they are the same length, then you need to check each version. There is no need for a specialCompare method when the version lengths are the same. Simply check each version and if they are the same, then go to the next version number and so on. As soon as one version is different then you will know what to return. If you go through the whole array then you know all the version numbers are the same.
Below is a change to the compareVersion method using the logic above. There is no need for a specialCompare method. I am guessing this is what you are looking for.
public static int compareVersion(String version1, String version2)
{
String[] arr1 = version1.split("\\.");
String[] arr2 = version2.split("\\.");
if (arr1.length < arr2.length)
return -1;
if (arr1.length > arr2.length)
return 1;
// same number of version "." dots
for (int i = 0; i < arr1.length; i++)
{
if(Integer.parseInt(arr1[i]) < Integer.parseInt(arr2[i]))
return -1;
if(Integer.parseInt(arr1[i]) > Integer.parseInt(arr2[i]))
return 1;
}
// went through all version numbers and they are all the same
return 0;
}
package com.e;
import java.util.*;
/**
* Created by dpc on 17-2-27.
*
*/
public class VersionComparator implements Comparator {
#Override
public int compare(String o1, String o2) {
if (o1 == null && o2 == null) {
return 0;
} else if (o1 == null && o2 != null) {
return -1;
} else if (o1 != null && o2 == null) {
return 1;
} else {
if (o1.length() == 0 && o2.length() == 0) {
return 0;
} else if (o1.length() == 0 && o2.length() > 0) {
return -1;
} else if (o1.length() > 0 && o2.length() == 0) {
return 1;
} else {
return compareVersion(o1, o2);
}
}
}
public static int compareVersion(String version1, String version2) {
String[] arr1 = version1.split("\\.");
String[] arr2 = version2.split("\\.");
try {
int i = 0;
while (i < arr1.length || i < arr2.length) {
if (i < arr1.length && i < arr2.length) {
if (Integer.parseInt(arr1[i]) < Integer.parseInt(arr2[i])) {
return -1;
} else if (Integer.parseInt(arr1[i]) > Integer.parseInt(arr2[i])) {
return 1;
} else if (Integer.parseInt(arr1[i]) == Integer.parseInt(arr2[i])) {
int result = specialCompare(version1, version2);
if (result != 0) {
return result;
}
}
} else if (i < arr1.length) {
if (Integer.parseInt(arr1[i]) != 0) {
return 1;
}
} else if (i < arr2.length) {
if (Integer.parseInt(arr2[i]) != 0) {
return -1;
}
}
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static int specialCompare(String str1, String str2) {
String[] arr1 = str1.split("\\.");
String[] arr2 = str2.split("\\.");
for (int i = 1; i < arr1.length; i++) {
if (Integer.parseInt(arr1[i]) != 0) {
return 0;
}
}
for (int j = 1; j < arr2.length; j++) {
if (Integer.parseInt(arr2[j]) != 0) {
return 0;
}
}
if (arr1.length < arr2.length) {
return -1;
} else {
return 1;
}
}
// test
public static List<String> getLowerList(String str, Comparator<String> comparator, List<String> list) {
if (str == null) {
return list;
}
List<String> newlist = new ArrayList<String>();
newlist.add(str);
newlist.addAll(list);
// sort
Collections.sort(newlist, comparator);
// search
int endIndex = Collections.binarySearch(newlist, str);
if (endIndex >= 0) {
// sublist 0 1
return newlist.subList(0, endIndex + 1);
} else {
return new ArrayList<String>();
}
}
public static void main(String[] args) {
List<String> test1 = Arrays.asList(new String[]{
"2.1.1", "1.21.22", "1.21.25", "1.113", "0.0.0.1",
"2.0.0", "1.2", "2.0", "0.1", "1.2.1", "1.1.1",
"11", "100", "" + Integer.MAX_VALUE + ".1", "",
"2.0", "10.1"});
List<String> test2 = Arrays.asList(new String[]{"", null, "0", "10.20.100", "3.1.1", "9.8", "10.3.92"});
List<String> newlist = new ArrayList<String>();
newlist.addAll(test1);
newlist.addAll(test2);
Collections.sort(newlist, new VersionComparator());
VersionComparator compareVersion = new VersionComparator();
System.out.println(newlist);
System.out.println(getLowerList("2", compareVersion, newlist));
System.out.println(getLowerList("3", compareVersion, newlist));
System.out.println(getLowerList("4", compareVersion, newlist));
System.out.println(getLowerList("5", compareVersion, newlist));
}
}

How to compare to BigInts in Java to determine which one is the larger BigInt

I'm trying to compare two BigInts that were manually created and did not use the built-in BigInt class. Right now I'm getting hung up trying to be able to determine how to find the bigger number of the 2. For example if I want to find which number is bigger between 123 and 134, and I pass in both BigInts I want to return a false if 123 is the first number passed or True if the second number is passed. Please see the code below:
private boolean thisBigIntGreaterThanOrEqualToOther(BigInt b1, BigInt b2){
boolean value = true;
if(b1.bigInt.size() >= b2.bigInt.size()){
for(int i = 0; i < b2.bigInt.size(); i++){
if(b1.bigInt.get(i) >= b2.bigInt.get(i)){
value = true;
}
else{
value = false;
}
}
}
else{
value = false;
}
return value;
}
As you can see in my code I thought about trying to compare each digit, but I run into an issue when I get to 1's for each number, it sets the value to true.
BigInt Class Below:
public class BigInt {
//Instance variables for the class, a boolean for the sign and an ArrayList to store the input String
private boolean pos = true;
private ArrayList<Integer> bigInt = new ArrayList<Integer>();
//No argument constructor, creates a big integer of value zero
public BigInt () {
this.pos = true;
}
//Constructor for big integers input as int
public BigInt (int newBigInt) {
String inputInt = Integer.toString(newBigInt);
inputInt = handleSign(inputInt);
inputInt = checkNumber(inputInt);
for(int i = inputInt.length() - 1; i >=0; i--) {
bigInt.add(Integer.parseInt(inputInt.substring(i, i+1)));
}
}
//Constructor for big integers input as strings
public BigInt (String newBigInt) {
newBigInt = handleSign(newBigInt);
newBigInt = checkNumber(newBigInt);
for(int i = newBigInt.length() - 1; i >=0; i--) {
bigInt.add(Integer.parseInt(newBigInt.substring(i, i+1)));
}
}
private String handleSign(String num) {
if(num.charAt(0) == '+' || num.charAt(0) == '-') {
if(num.length() == 1) {
throw new ErrorMessage("Invalid value: sign only, no integer.");
}
if(num.charAt(0) == '-') {
this.pos = false;
}
else {
this.pos = true;
}
num = num.substring(1);
}
return num;
}
// Private method to remove leading zeros from add/subtract methods
private BigInt removeZeros(BigInt result){
for(int i = 0; i < result.bigInt.size(); i++){
if(result.bigInt.get(i) == 0){
result.bigInt.remove(i);
}
}
return result;
}
//Private method to check the number; remove leading zeros and check for leading spaces (throw error message)
private String checkNumber(String num) {
if(num.charAt(0) == ' ') {
throw new ErrorMessage("Invalid value: leading blank space.");
}
if(num.charAt(0) == '0'){
while(num.length() > 1 && num.charAt(0) == '0') {
num = num.substring(1);
}
}
return num;
}
//toString method
public String toString() {
String answer = "";
for(int i = bigInt.size() - 1; i >=0; i--) {
answer = answer + bigInt.get(i);
}
if(this.pos == false){
return "-" + answer;
}
return answer;
The method to compare two BigInts is broken in several ways:
1. You iterate in the wrong direction:
for(int i = 0; i < b2.bigInt.size(); i++)
You start from the least significant digit which means 20 would be considered smaller than 11. Change it to
for(int i = b2.bigInt.size() - 1; i >= 0 ; i--)
2. You override the result of the comparison
If your code reaches the point where it sets value = false; it does not return or exit the loop. That means that in the next iteration the value gets overriden. That means suddenly 13 and 23 are considered equal.
BigInt c = new BigInt("13");
BigInt d = new BigInt("23");
System.out.println(BigInt.thisBigIntGreaterThanOrEqualToOther(c, d));
System.out.println(BigInt.thisBigIntGreaterThanOrEqualToOther(d, c));
The output is
true
true
Change value = false; to return false;
3. You do not check whether b1.bigInt.size() > b2.bigInt.size()
This results in your method returning that 131 is smaller than 23.
Change your code in the following way:
if(b1.bigInt.size() > b2.bigInt.size()){
return true;
} else if(b1.bigInt.size() < b2.bigInt.size()){
return false;
} else {
// the other comparison code
}
Some final remarks:
It is good design to implement the Comparable interface as it allows you to use many library methods with your class.
EDIT: code now does not use library functions anymore
public class BigInt implements Comparable<BigInt> {
...
#Override
public int compareTo(BigInt other) {
int c = this.bigInt.size() - other.bigInt.size();
if (c != 0) {
return c;
} else {
for (int i = this.bigInt.size() - 1; i >= 0; i--) {
c = this.bigInt.get(i) - other.bigInt.get(i);
if (c != 0) {
return c;
}
}
return 0;
}
}
}

Arrange char array in sequence

I came across a post showing how to arrange char array by alphabet order.
seeing this can be done, I want to output the alphabetical order of each character of the input string, in order of the characters of the input string.
I'm a bit stuck. I can get the string reordered alphabetically, but I don't know what to do next.
example is 'monkey' to '354216'
because 'ekmnoy' e is alphabetically first from the set of given characters so e = 1 , k is the second alpha char when sorted so k = 2, and so on.
if you cannot understand I can provide more example to make things clear out.
Code
String str = "airport";
Character[] chars = new Character[str.length()];
for (int z = 0; z < chars.length; z++) {
chars[z] = str.charAt(z);
}
Arrays.sort(chars, new Comparator<Character>() {
public int compare(Character c1, Character c2) {
int cmp = Character.compare(
Character.toLowerCase(c1.charValue()),
Character.toLowerCase(c2.charValue()));
if (cmp != 0) {
return cmp;
}
return Character.compare(c1.charValue(), c2.charValue());
}
});
StringBuilder sb = new StringBuilder(chars.length);
for (char c : chars) {
sb.append(c);
}
str = sb.toString();
System.out.println(sb);
Output
aioprrt
expected output
Orange -> aegnOr
561432 - 123456
Monkey -> ekMnoy
354216 -> 123456
I dont know what you want to do with double characters, but if you add this few lines to your code at the end you are getting the right result. Iterate over the sorted String and replace the charakters in the original String with their indices in the sorted String.
String originalStr = "airport";
for(int i = 0; i<str.length(); i++) {
originalStr = originalStr.replace(str.charAt(i), String.valueOf(i+1).charAt(0));
}
System.out.println(originalStr);
Output: 1254357
If you want to get the output: 1254367 use replaceFirst:
originalStr = originalStr.replaceFirst(String.valueOf(str.charAt(i)), String.valueOf(i+1));
Input:Orange
Output:561432
Input:Monkey
Output:354216
The whole code:
String str = "airport";
String originalStr = str; //creat a backup of str because you change it in your code
Character[] chars = str.toCharArray();
Arrays.sort(chars, new Comparator<Character>() {
public int compare(Character c1, Character c2) {
int cmp = Character.compare(
Character.toLowerCase(c1.charValue()),
Character.toLowerCase(c2.charValue()));
if (cmp != 0) {
return cmp;
}
return Character.compare(c1.charValue(), c2.charValue());
}
});
str = String.valueOf(chars);
System.out.println(str);
//Iterate over the sorted String and replace the charakters in the original String with their indices in the sorted String
for(int i = 0; i<str.length(); i++) {
originalStr = originalStr.replaceFirst(String.valueOf(str.charAt(i)), String.valueOf(i+1));
}
System.out.println(originalStr);
Once you have arranged the characters in order (in a different array from the original) then create a third array by walking the original string and choosing the index of each character from te sorted string.
input: edcba
sorted: abcde
index: 01234
Pseudocode...
for( int i = 0; i < input.length(); i++ ) {
index[i] = sorted.indexOf(input[i]);
}
Result should be 43210 with the given input.
Note that strings with more than 10 characters will result in ambiguous output, which can be handled by inserting spaces in the output. Example:
abcdefghijk ->
012345678910
You can use this below code:
package Test;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class Arrange {
public static void main(String[] args) {
String str = "money";
List<Test> strs=new LinkedList<Test>();
List<Test> final_result=new LinkedList<Test>();
for(int i=0;i<str.length();i++)
{
Test t=new Test(i, ""+str.charAt(i), 0);
strs.add(t);
}
Collections.sort(strs,new Comparator<Test>() {
#Override
public int compare(Test o1, Test o2) {
return (o1.getS().compareToIgnoreCase(o2.getS()));
}
});
Integer i=1;
for (Test st : strs) {
st.setJ(i);
final_result.add(st);
i++;
}
Collections.sort(final_result,new Comparator<Test>() {
#Override
public int compare(Test o1, Test o2) {
return (o1.getI().compareTo(o2.getI()));
}
});
for (Test test : final_result) {
System.out.println(test.getJ());
}
}
}
class Test{
private Integer i;
private String s;
private Integer j;
public Test() {
// TODO Auto-generated constructor stub
}
public Test(Integer i, String s, Integer j) {
super();
this.i = i;
this.s = s;
this.j = j;
}
public Integer getI() {
return i;
}
public void setI(Integer i) {
this.i = i;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
public Integer getJ() {
return j;
}
public void setJ(Integer j) {
this.j = j;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((i == null) ? 0 : i.hashCode());
result = prime * result + ((j == null) ? 0 : j.hashCode());
result = prime * result + ((s == null) ? 0 : s.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Test other = (Test) obj;
if (i == null) {
if (other.i != null)
return false;
} else if (!i.equals(other.i))
return false;
if (j == null) {
if (other.j != null)
return false;
} else if (!j.equals(other.j))
return false;
if (s == null) {
if (other.s != null)
return false;
} else if (!s.equals(other.s))
return false;
return true;
}
}

Validate a String to see if it is an Integer?

Not keen on using the parseInteger solution, it is ugly, and as Joshua Bloch says you should "Use exceptions only for exceptional conditions". Of course, I can use something like block of code below, but it doesn't guarantee it is an Integer.
for (char c : str.toCharArray())
{
if (!Character.isDigit(c)) return false;
}
return true;
"Use exceptions only for exceptional conditions" is a good practice to follow in general, but it's not a hard-and-fast rule. I think that this is one of the cases where using exceptions is better than the alternatives.
Since parseInteger() can return any possible int value, you can't use any other return value to indicate failure. If you know you're never going to process a particular value (such as -1 or -2147483648), you can return that as a sentinel value to indicate a parse failure.
The only alternative is to return a boolean indicating success or failure and to store the parsed value into a parameter. However, since function calls are always pass-by-value in Java, you'd need to create a new class to do this:
public class IntWrapper
{
int value;
}
...
public static boolean myParseInt(String s, IntWrapper outValue)
{
try
{
outValue.value = Integer.parseInt(s);
return true;
}
catch(NumberFormatException e)
{
return false;
}
}
...
IntWrapper value = new IntWrapper();
if (myParseInt(value))
{
// Use value.value
}
else
{
// Parsing failed
}
Given these alternatives, I think the simplest usage is just to use exceptions and deal with them appropriately, even though non-numeric input may not necessary be an "exceptional" condition.
I'd leave it with exception but if you REALLY want solution without exception you can copy method parseInt() from this site with java internal classes and change it a little bit
(You can modify it a little bit more, since you do not need result)
public static false isValidInt(String s, int radix)
throws NumberFormatException
{
if (s == null) {
return false;
}
if (radix < Character.MIN_RADIX) {
return false;
}
if (radix > Character.MAX_RADIX) {
return false;
}
int result = 0;
boolean negative = false;
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else
return false;
if (len == 1) // Cannot have lone "-"
return false;
i++;
}
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
return false;
}
if (result < multmin) {
return false;
}
result *= radix;
if (result < limit + digit) {
return false;
}
result -= digit;
}
} else {
return false;
}
return true;
}
You could use:
public static boolean isInteger(String str) {
if (str == null) {
return false;
}
int length = str.length();
if (length == 0) {
return false;
}
int i = 0;
if (str.charAt(0) == '-') {
if (length == 1) {
return false;
}
i = 1;
}
for (; i < length; i++) {
char c = str.charAt(i);
if (c <= '/' || c >= ':') {
return false;
}
}
return true;
}
Already answered here: What's the best way to check to see if a String represents an integer in Java?

Categories

Resources