objects - object compare to java
Manera eficiente de comparar cadenas de versiĆ³n en Java (10)
Adaptado de la respuesta de Alex Gitelman.
int compareVersions( String str1, String str2 ){
if( str1.equals(str2) ) return 0; // Short circuit when you shoot for efficiency
String[] vals1 = str1.split("//.");
String[] vals2 = str2.split("//.");
int i=0;
// Most efficient way to skip past equal version subparts
while( i<vals1.length && i<val2.length && vals[i].equals(vals[i]) ) i++;
// If we didn''t reach the end,
if( i<vals1.length && i<val2.length )
// have to use integer comparison to avoid the "10"<"1" problem
return Integer.valueOf(vals1[i]).compareTo( Integer.valueOf(vals2[i]) );
if( i<vals1.length ){ // end of str2, check if str1 is all 0''s
boolean allZeros = true;
for( int j = i; allZeros & (j < vals1.length); j++ )
allZeros &= ( Integer.parseInt( vals1[j] ) == 0 );
return allZeros ? 0 : -1;
}
if( i<vals2.length ){ // end of str1, check if str2 is all 0''s
boolean allZeros = true;
for( int j = i; allZeros & (j < vals2.length); j++ )
allZeros &= ( Integer.parseInt( vals2[j] ) == 0 );
return allZeros ? 0 : 1;
}
return 0; // Should never happen (identical strings.)
}
Así que como puedes ver, no es tan trivial. También esto falla cuando permites los 0 primeros, pero nunca he visto una versión "1.04.5" o w / e. Necesitaría usar la comparación de enteros en el bucle while para arreglar eso. Esto se vuelve aún más complejo cuando mezcla letras con números en las cadenas de versión.
Posible duplicado:
¿Cómo se comparan dos cadenas de versión en Java?
Tengo 2 cadenas que contienen información de la versión como se muestra a continuación:
str1 = "1.2"
str2 = "1.1.2"
Ahora, cualquiera puede decirme la manera eficiente de comparar estas versiones dentro de las cadenas en Java y devolver 0, si son iguales, -1, si str1 <str2 & 1 si str1> str2.
Como han señalado otros, String.split () es una manera muy fácil de hacer la comparación que desea, y Mike Deck hace el punto excelente de que con tales cadenas (probablemente) cortas, probablemente no importe mucho, pero lo que el ¡Oye! Si desea realizar la comparación sin analizar manualmente la cadena y tiene la opción de salir antes, puede probar la clase java.util.Scanner .
public static int versionCompare(String str1, String str2) {
try ( Scanner s1 = new Scanner(str1);
Scanner s2 = new Scanner(str2);) {
s1.useDelimiter("//.");
s2.useDelimiter("//.");
while (s1.hasNextInt() && s2.hasNextInt()) {
int v1 = s1.nextInt();
int v2 = s2.nextInt();
if (v1 < v2) {
return -1;
} else if (v1 > v2) {
return 1;
}
}
if (s1.hasNextInt() && s1.nextInt() != 0)
return 1; //str1 has an additional lower-level version number
if (s2.hasNextInt() && s2.nextInt() != 0)
return -1; //str2 has an additional lower-level version
return 0;
} // end of try-with-resources
}
Comparar cadenas de versión puede ser un desastre; está obteniendo respuestas inútiles porque la única forma de hacer que esto funcione es ser muy específico acerca de cuál es su convención de pedidos. He visto una función de comparación de versiones relativamente corta y completa en una publicación de blog , con el código colocado en el dominio público, no está en Java, pero debería ser sencillo ver cómo adaptar esto.
Dividir la cadena en "." o cualquiera que sea su delimitador, analice cada uno de esos tokens con el valor Integer y compare.
int compareStringIntegerValue(String s1, String s2, String delimeter)
{
String[] s1Tokens = s1.split(delimeter);
String[] s2Tokens = s2.split(delimeter);
int returnValue = 0;
if(s1Tokens.length > s2Tokens.length)
{
for(int i = 0; i<s1Tokens.length; i++)
{
int s1Value = Integer.parseString(s1Tokens[i]);
int s2Value = Integer.parseString(s2Tokens[i]);
Integer s1Integer = new Integer(s1Value);
Integer s2Integer = new Integer(s2Value);
returnValue = s1Integer.compareTo(s2Value);
if( 0 == isEqual)
{
continue;
}
return returnValue; //end execution
}
return returnValue; //values are equal
}
Dejaré la otra si la declaración como un ejercicio.
Dividiría el problema en dos, formando y comparando. Si puede asumir que el formato es correcto, entonces comparar solo la versión de los números es muy simple:
final int versionA = Integer.parseInt( "01.02.00".replaceAll( "//.", "" ) );
final int versionB = Integer.parseInt( "01.12.00".replaceAll( "//.", "" ) );
Entonces ambas versiones se pueden comparar como enteros. Entonces el "gran problema" es el formato, pero puede tener muchas reglas. En mi caso, acabo de completar un mínimo de dos pares de dígitos, por lo que el formato es "99.99.99" siempre, y luego hago la conversión anterior; así que en mi caso la lógica del programa está en el formato, y no en la comparación de versiones. Ahora, si está haciendo algo muy específico y tal vez puede confiar en el origen de la cadena de versión, tal vez pueda verificar la longitud de la cadena de versión y luego hacer la conversión int ... pero creo que es una buena práctica Asegúrese de que el formato sea el esperado.
Dividirlos en matrices y luego comparar.
// check if two strings are equal. If they are return 0;
String[] a1;
String[] a2;
int i = 0;
while (true) {
if (i == a1.length && i < a2.length) return -1;
else if (i < a1.length && i == a2.length) return 1;
if (a1[i].equals(a2[i]) {
i++;
continue;
}
return a1[i].compareTo(a2[i];
}
return 0;
Es casi seguro que esta no es la forma más eficiente de hacerlo, pero dado que las cadenas de números de versión casi siempre tendrán solo unos pocos caracteres, no creo que valga la pena seguir optimizando:
public static int compareVersions(String v1, String v2) {
String[] components1 = v1.split("//.");
String[] components2 = v2.split("//.");
int length = Math.min(components1.length, components2.length);
for(int i = 0; i < length; i++) {
int result = new Integer(components1[i]).compareTo(Integer.parseInt(components2[i]));
if(result != 0) {
return result;
}
}
return Integer.compare(components1.length, components2.length);
}
Estaba tratando de hacer esto yo mismo y veo tres enfoques diferentes para hacer esto, y hasta ahora casi todo el mundo está dividiendo las cadenas de versión. No veo que hacer eso sea eficiente, aunque el tamaño del código se lee bien y se ve bien.
Enfoques:
- Suponga un límite superior al número de secciones (ordinales) en una cadena de versión, así como un límite al valor representado allí. A menudo, 4 puntos máximo y 999 máximo para cualquier ordinal. Puede ver a dónde va esto y se va a transformar la versión para que se ajuste a una cadena como: "1.0" => "001000000000" con formato de cadena o alguna otra forma de rellenar cada ordinal. Luego haz una comparación de cuerdas.
- Divida las cadenas en el separador ordinal (''.'') Y repita sobre ellas y compare una versión analizada. Este es el enfoque bien demostrado por Alex Gitelman.
- Comparando los ordinales a medida que los analiza fuera de las cadenas de versión en cuestión. Si todas las cadenas fueran en realidad solo punteros a matrices de caracteres como en C, entonces este sería el enfoque claro (donde reemplazaría un ''.'' Con un terminador nulo como se encuentra y movería unos 2 o 4 punteros alrededor.
Reflexiones sobre los tres enfoques:
- Había una publicación de blog vinculada que mostraba cómo ir con 1. Las limitaciones están en la longitud de la cadena de versión, el número de secciones y el valor máximo de la sección. No creo que sea una locura tener una cuerda que rompa 10,000 en un punto. Además, la mayoría de las implementaciones todavía terminan dividiendo la cadena.
- Dividir las cadenas de antemano es fácil de leer y pensar, pero estamos repasando cada cadena aproximadamente dos veces para hacer esto. Me gustaría comparar cómo se compara con el siguiente enfoque.
- La comparación de la cadena a medida que la divide le brinda la ventaja de poder detener la división muy pronto en una comparación de: "2.1001.100101.9999998" a "1.0.0.0.0.0.1.0.0.0.1". Si esto fuera C y no Java, las ventajas podrían continuar limitando la cantidad de memoria asignada para nuevas cadenas para cada sección de cada versión, pero no lo es.
No vi a nadie dando un ejemplo de este tercer enfoque, así que me gustaría agregarlo aquí como una respuesta que apunta a la eficiencia.
public class VersionHelper {
/**
* Compares one version string to another version string by dotted ordinals.
* eg. "1.0" > "0.09" ; "0.9.5" < "0.10",
* also "1.0" < "1.0.0" but "1.0" == "01.00"
*
* @param left the left hand version string
* @param right the right hand version string
* @return 0 if equal, -1 if thisVersion < comparedVersion and 1 otherwise.
*/
public static int compare(@NotNull String left, @NotNull String right) {
if (left.equals(right)) {
return 0;
}
int leftStart = 0, rightStart = 0, result;
do {
int leftEnd = left.indexOf(''.'', leftStart);
int rightEnd = right.indexOf(''.'', rightStart);
Integer leftValue = Integer.parseInt(leftEnd < 0
? left.substring(leftStart)
: left.substring(leftStart, leftEnd));
Integer rightValue = Integer.parseInt(rightEnd < 0
? right.substring(rightStart)
: right.substring(rightStart, rightEnd));
result = leftValue.compareTo(rightValue);
leftStart = leftEnd + 1;
rightStart = rightEnd + 1;
} while (result == 0 && leftStart > 0 && rightStart > 0);
if (result == 0) {
if (leftStart > rightStart) {
return containsNonZeroValue(left, leftStart) ? 1 : 0;
}
if (leftStart < rightStart) {
return containsNonZeroValue(right, rightStart) ? -1 : 0;
}
}
return result;
}
private static boolean containsNonZeroValue(String str, int beginIndex) {
for (int i = beginIndex; i < str.length(); i++) {
char c = str.charAt(i);
if (c != ''0'' && c != ''.'') {
return true;
}
}
return false;
}
}
Prueba unitaria que demuestra la salida esperada.
public class VersionHelperTest {
@Test
public void testCompare() throws Exception {
assertEquals(1, VersionHelper.compare("1", "0.9"));
assertEquals(1, VersionHelper.compare("0.0.0.2", "0.0.0.1"));
assertEquals(1, VersionHelper.compare("1.0", "0.9"));
assertEquals(1, VersionHelper.compare("2.0.1", "2.0.0"));
assertEquals(1, VersionHelper.compare("2.0.1", "2.0"));
assertEquals(1, VersionHelper.compare("2.0.1", "2"));
assertEquals(1, VersionHelper.compare("0.9.1", "0.9.0"));
assertEquals(1, VersionHelper.compare("0.9.2", "0.9.1"));
assertEquals(1, VersionHelper.compare("0.9.11", "0.9.2"));
assertEquals(1, VersionHelper.compare("0.9.12", "0.9.11"));
assertEquals(1, VersionHelper.compare("0.10", "0.9"));
assertEquals(0, VersionHelper.compare("0.10", "0.10"));
assertEquals(-1, VersionHelper.compare("2.10", "2.10.1"));
assertEquals(-1, VersionHelper.compare("0.0.0.2", "0.1"));
assertEquals(1, VersionHelper.compare("1.0", "0.9.2"));
assertEquals(1, VersionHelper.compare("1.10", "1.6"));
assertEquals(0, VersionHelper.compare("1.10", "1.10.0.0.0.0"));
assertEquals(1, VersionHelper.compare("1.10.0.0.0.1", "1.10"));
assertEquals(0, VersionHelper.compare("1.10.0.0.0.0", "1.10"));
assertEquals(1, VersionHelper.compare("1.10.0.0.0.1", "1.10"));
}
}
Requiere commons-lang3-3.8.1.jar para operaciones de cadena.
/**
* Compares two version strings.
*
* Use this instead of String.compareTo() for a non-lexicographical
* comparison that works for version strings. e.g. "1.10".compareTo("1.6").
*
* @param v1 a string of alpha numerals separated by decimal points.
* @param v2 a string of alpha numerals separated by decimal points.
* @return The result is 1 if v1 is greater than v2.
* The result is 2 if v2 is greater than v1.
* The result is -1 if the version format is unrecognized.
* The result is zero if the strings are equal.
*/
public int VersionCompare(String v1,String v2)
{
int v1Len=StringUtils.countMatches(v1,".");
int v2Len=StringUtils.countMatches(v2,".");
if(v1Len!=v2Len)
{
int count=Math.abs(v1Len-v2Len);
if(v1Len>v2Len)
for(int i=1;i<=count;i++)
v2+=".0";
else
for(int i=1;i<=count;i++)
v1+=".0";
}
if(v1.equals(v2))
return 0;
String[] v1Str=StringUtils.split(v1, ".");
String[] v2Str=StringUtils.split(v2, ".");
for(int i=0;i<v1Str.length;i++)
{
String str1="",str2="";
for (char c : v1Str[i].toCharArray()) {
if(Character.isLetter(c))
{
int u=c-''a''+1;
if(u<10)
str1+=String.valueOf("0"+u);
else
str1+=String.valueOf(u);
}
else
str1+=String.valueOf(c);
}
for (char c : v2Str[i].toCharArray()) {
if(Character.isLetter(c))
{
int u=c-''a''+1;
if(u<10)
str2+=String.valueOf("0"+u);
else
str2+=String.valueOf(u);
}
else
str2+=String.valueOf(c);
}
v1Str[i]="1"+str1;
v2Str[i]="1"+str2;
int num1=Integer.parseInt(v1Str[i]);
int num2=Integer.parseInt(v2Str[i]);
if(num1!=num2)
{
if(num1>num2)
return 1;
else
return 2;
}
}
return -1;
}
Paso 1: use StringTokenizer en java con un punto como delimitador
StringTokenizer(String str, String delimiters)
o
Puede usar String.split()
y Pattern.split()
, dividir en punto y luego convertir cada String a Integer usando Integer.parseInt(String str)
Paso 2: Compara el entero de izquierda a derecha.