miércoles, 19 de octubre de 2011

Extraer un caracter de una cadena en java





En ocasiones, es necesario extraer un caracter de una cadena. Existen varias formas de realizar esta accion, una de ellas, es la siguiente:

String s = "Hola mundo";
  s.substring(0,1);

Sin embargo, la forma optima de realizar esta tarea, es utilizado la funcion charAt.

String s = "Hola mundo";
  s.charAt(0);

La principal ventaja en utilizar esta ultima funcion, radica en que se obtiene un valor directamente, mientras que en el primer codigo, se obtiene un objeto, por lo que al comparar, en el primer caso, es necesario utilizar la funcion equals, mientras que en la segunda se utiliza el operador de comparacion ==, derivado de lo mismo (al menos hasta java 1.6), el valor retornado por la funcion charAt, se puede utilizar dentro de una instruccion switch.

domingo, 21 de agosto de 2011

Concatenacion de dos cadenas (concat)

La concatenacion de cadenas, es una operacion ampliamente utilizada en los lenguajes de programacion. Pero en el caso de algunos lenguajes compilados, existen metodos poco utilizados, pero muy utiles. Un ejemplo es el metodo java.lant.String.concat o System.String.Concat que tienen algunos lenguajes de programación como java o C#. Veamos por que.


Cuando es necesario concatenar exactamente 2 cadenas y utilizar inmediatamente el resultado de esto (por ejemplo, agregar una extension a un nombre de archivo, para crearlo) es recomendable utilizar este metodo y no algun otro alternativo, como el uso de operadores o de clases auxiliares, como lo es java.lang.StringBuilder o System.StringBuilder.

¿Que ocurre, cuando se emplea StringBuilder?
  1. Creacion del objeto StringBuilder, inicializado, con la primer cadena (Creacion del primer objeto)
  2. Agregar los elementos caracteres de la segunda cadena (posiblemente expandiendo el buffer del builder, posiblemente creando el segundo objeto implicito, un arreglo de caracteres).
  3. Crear un nuevo objeto cadena (Creacion del segundo o tercer objeto)
¿Que ocurre cuando se emplea el metodo concat?
  1. Cracion de un nuevo objeto cadena, con la longitud exacta de la suma de ambos elementos (Creacion del primer y unico nuevo objeto)
  2. Copiar los elementos de la primer cadena
  3. Copiar los elementos de la segunda cadena
Cuando se trata de concatenar exactamente dos cadenas, el metodo concat es optimo, en terminos de uso de memoria, por que solo utiliza la cantidad exacta de memoria, mientras que la clase StringBuilder, al ser generica (para concatenar cualquier cantidad de cadenas) aloja bloques de memoria de tamaños distintos, tratando de evitar alojar memoria posteriormente (normalmente, son bloques cada vez mas grandes) y al finalmente, crea un objeto String, con el resultado de la concatenacion.

Nota: Dependiendo del lenguaje de programacion, asi como la version del mismo, el metodo de concatenacion, puede o no tener alguna de las siguientes optimizaciones.

  •  Si el metodo recibe dos parametros exactamente y es miembro estatico
    • Si ambos parametros son nulos, se regresa la cadena vacia.
    • Cuando alguno de los dos parametros es nulo, se regresa el otro parametro.
    • Cuando alguno de los dos parametros es la cadena vacia, se regresa el otro parametro. 
  • Cuando el metodo no es estatico y se invoca utilizando un objeto de la clase String
    • Si el parametro que se recibe es nulo, se regresa la cadena referenciada por this.
    • Si el parametro que se recibe es la cadena vacia, se regresa la cadena referenciada por this.

viernes, 15 de julio de 2011

Utilizar la memoria RAM como cache en Linux (ramdisk)

La mayoria de las distribuciones de linux cuentan con la capacidad de utilizar la memoria de acceso aleatorio como un sistema de archivos normal, donde se puede crear, leer, escribir y borrar un archivo, asi como crear directorios y borrarlos.

Lo primero que hay que ubicar, es que existan los archivos que inician con el prefijo ram, en el directorio /dev. Al ejecutar el listado de estos archivos, se obtiene una salida similar a esta:

   $ ls -l /dev/ram*
   brw-rw----. 1 root disk 1, 0 Jul 14 22:36 /dev/ram0
   brw-rw----. 1 root disk 1, 1 Jul 14 22:49 /dev/ram1
   brw-rw----. 1 root disk 1, 10 Jul 14 22:36 /dev/ram10
   brw-rw----. 1 root disk 1, 11 Jul 14 22:36 /dev/ram11
   brw-rw----. 1 root disk 1, 12 Jul 14 22:36 /dev/ram12
   brw-rw----. 1 root disk 1, 13 Jul 14 22:36 /dev/ram13
   brw-rw----. 1 root disk 1, 14 Jul 14 22:36 /dev/ram14
   brw-rw----. 1 root disk 1, 15 Jul 14 22:36 /dev/ram15
   brw-rw----. 1 root disk 1, 2 Jul 14 22:36 /dev/ram2
   brw-rw----. 1 root disk 1, 3 Jul 14 22:36 /dev/ram3
   brw-rw----. 1 root disk 1, 4 Jul 14 22:36 /dev/ram4
   brw-rw----. 1 root disk 1, 5 Jul 14 22:36 /dev/ram5
   brw-rw----. 1 root disk 1, 6 Jul 14 22:36 /dev/ram6
   brw-rw----. 1 root disk 1, 7 Jul 14 22:36 /dev/ram7
   brw-rw----. 1 root disk 1, 8 Jul 14 22:36 /dev/ram8
   brw-rw----. 1 root disk 1, 9 Jul 14 22:36 /dev/ram9

Para formatear y utilizar estos archivos, es necesario hacerlo como el usuario root. Esto, debido a que el propietario de los archivos es root.

   $ su -
   Password:
   # mkfs.ext2 -q /dev/ram1
   # mkdir tmp
   # mount /dev/ram1 tmp
   # df -k tmp
   Filesystem 1K-blocks Used Available Use% Mounted on
   /dev/ram1 15863 140 14904 1% /root/tmp

Ahora en el directorio tmp, se tiene un sistema de archivos, que utiliza la memoria de acceso aleatorio, como un directorio normal dentro del sistema. Cabe destacar que este directorio, sera destruido cuando se reinicie el sistema, por lo que cualquier informacion importante, debe ser respaldada previamente antes de reiniciar.


---

jueves, 14 de julio de 2011

Utilizar la memoria RAM como cache automatico en Linux

En los sistemas linux, el uso de memoria RAM como cache, es gestionado por el kernel de manera automatica, sin necesidad de configurar o ejecutar los programas de manera distinta a la usual.

Veamos el siguiente ejemplo, en el cual, se tiene un archivo de 25.85M aproximadamente.

   $ ls -l archivo.bin
   -rwxr-xr-x. 1 cmartine cmartine 27109840 Jul  1 10:52 archivo.bin

Utilizando el comando time, para determinar el tiempo de ejecucion y el comando cat para realizar la lectura del archivo, obtenemos lo siguiente en la primer ejecucion. Nota: se envia la salida al dispositivo /dev/null, para descartarla.

   $ time cat archivo.bin > /dev/null
   real   0m0.466s
   user   0m0.002s
   sys   0m0.123s

El tiempo que se tardo en ejecutar fue de 0.466 segundo. Posteriormente, ejecutando el mismo comando inmediatamente 3 veces seguidas, se obtiene el siguiente resultado:

   $ time cat archivo.bin > /dev/null
   real    0m0.040s
   user    0m0.002s
   sys     0m0.037s

   $ time cat archivo.bin > /dev/null
   real    0m0.026s
   user    0m0.001s
   sys     0m0.025s

   $ time cat archivo.bin > /dev/null
   real    0m0.029s
   user    0m0.000s
   sys     0m0.025s

Como se puede observar, el tiempo real, disminuyo drasticamente a menos de la decima parte del tiempo original. Esto es debido a que el kernel de linux maneja de manera automatica la memoria RAM que no se emplea en crear un cache de los archivos utilizados, sin necesidad de realizar configuracion alguna. La cantidad de memoria que esta siendoutilizada en este cache, se puede consultar utilizando el comando top.

¿Por que linux hace esto? Sencillamente por que si no se emplea la memoria, estaria desocupada (desperdiciandose cuando el sistema no la esta utilizando para realizar algun trabajo). Es importante aclara, que cuando se ejecuta un programa, el cual requiere utilizar mucha memoria, se libera parte de la memoria utilizada para el cache y se asigna al programa, pudiendo llegar a la situacion en que el cache ya no tenga memoria asignada, por que un programa requiere utilizar toda la memoria del sistema

jueves, 23 de junio de 2011

Comparacion entre 2 fechas

Comparar 2 fechas, para determinar cual es mayor, puede ser sencillo, sobre todo, si con lo que trabajas soporta el concepto de fecha y/o tiempo, por ejemplo: java.util.Date en java o System.DateTime en C#, que son lenguajes de programacion o incluso en hojas de datos, tipo Microsoft Excel o LibreOffice Calc.

El problema, se presenta, cuando es necesario trabajar con lenguajes que no conocen el concepto, como los shells de unix o cuando se almacenan archivos que contienen la fecha en el nombre del mismo.

En este punto la abstraccion de la fecha y la forma en como se representa, suele tomar un papel importante, dado que puede resolver completamente el problema, si se emplea el formato correcto.

Formas comunes de representar una fecha son: dia/mes/año en mexico o mes/dia/año en EEUU. Para nombres de archivos se suelen substituir las diagonales, por guiones, asi como el uso del nombre del mes y no el numero de mes. Por ejemplo:
  • Jun/27/2011
  • Oct/21/2011
  • Jul/1/2011
  • Oct/24/2005
  • Jul/4/2011
Si se ordena alfabeticamente, se obtiene lo siguiente:
  • Jul/1/2011
  • Jul/4/2011
  • Jun/27/2011
  • Oct/21/2011
  • Oct/24/2005
Lo cual no es lo que se requiere, sin embargo, si se reacomoda la fecha utilizando el orden año, mes, dia:
  • 2011/Jun/27
  • 2011/Oct/21
  • 2011/Jul/1
  • 2005/Oct/24
  • 2011/Jul/4
Al ordenar se obtiene lo siguiente:
  • 2005/Oct/24
  • 2011/Jul/1
  • 2011/Jul/4
  • 2011/Jun/27
  • 2011/Oct/21
El problema con el que nos encontramos es que la l esta antes que la n, en orden alfabetico, para sortear este inconveniente, en vez del nombre del mes, utilizamos el numero del mes:

  • 2011/6/27
  • 2011/10/21
  • 2011/7/1
  • 2005/10/24
  • 2011/7/4 
Al ordenar, obtenemos lo siguiente:

  • 2005/10/24
  • 2011/6/27
  • 2011/10/1
  • 2011/7/1
  • 2011/7/4
 Este resultado tampoco es correcto, esto es debido a que la longitud de los meses, es variable, por lo que, agregamos un 0 a la izquierda, cuando el mes esta formado por un solo digito (es recomendable tambien hacerlo para los dias)

  • 2011/06/27
  • 2011/10/21
  • 2011/07/01
  • 2005/10/24
  • 2011/07/04 
Al ordenar, obtenemos lo siguiente:

  • 2005/10/24
  • 2011/06/27
  • 2011/07/01
  • 2011/07/04
  • 2011/10/01
Que es finalmente lo que se requiere.

Esto, se puede aplicar a la creacion de nombres de archivos, por ejemplo, para poder ordenarlos por una fecha en particular, sobre todo cuando la fecha de creacion, es incorrecta (cuando son enviados por correo electronico y se pierde la fecha de creacion del archivo), por ejemplo los siguientes nombres de archivos seran ordenados correctamente por el sistema operativo al momento de listarlos (esto tanto en windows, linux o mac os)
  • reporte_2011-06-27.txt
  • reporte_2011-10-21.txt
  • reporte_2011-07-01.txt
  • reporte_2005-10-24.txt
  • reporte_2011-07-04.txt
 Al ser listados, se obtiene lo siguiente:
  • reporte_2005-10-24.txt
  • reporte_2011-06-27.txt
  • reporte_2011-07-01.txt
  • reporte_2011-07-04.txt
  • reporte_2011-10-21.txt
En unix, incluso, es posible emplear las fechas en este formato, con el comando expr, el cual puede comparar dos cadenas y determinar cual es mayor o menor.

jueves, 5 de mayo de 2011

Implementación la funcion compareTo de IComparable en C#






La interfaces IComparable e IComparable<t> de C#, se utilizan para comparar objetos, normalmente del mismo tipo y determinar, si alguno es mayor, menor o la equivalencia. A continuacion, se presentan 4 ejemplos de como implementar esta funcionalidad.

Nota1: Para el primer ejemplo, se utiliza el concepto de National Identification Number (CURP en México, RUN en Chile, DNI en España y Argentina o Tarjeta de Identidad en colombia, por ejemplo)
Nota2: Por razones de compatibilidad, se implementan 2 interfaces distintas, con funcionalidades similares, en la implementacion de estas clases.

class Person1 : System.IComparable, System.IComparable<Person1>
    {
        private string nin; // National Identification Number
        private string name;
        private string surname;

        //...

        public int CompareTo(object obj)
        {
            if (obj is Person1)
            {
                return obj == null ? 1 : nin.CompareTo(((Person1)obj).nin);
            }
            throw new System.ArgumentException();
        }

        public int CompareTo(Person1 p)
        {
            return p == null ? 1 : nin.CompareTo(p.nin);
        }

    }

    class Person2 : System.IComparable, System.IComparable<Person2>
    {
        private int id;
        private string name;
        private string surname;

        //...

        public int CompareTo(object obj)
        {
            if (obj is Person2)
            {
                return obj == null ? 1 : id.CompareTo(((Person2)obj).id);
            }
            throw new System.ArgumentException();
        }

        public int CompareTo(Person2 p)
        {
            return p == null ? 1 : id.CompareTo(p.id);
        }


    }

    class Person3 : System.IComparable, System.IComparable<Person3>
    {
        private int id;
        private string name;
        private string surname;

        //...

        public int CompareTo(object obj)
        {
            if (obj == null)
            {
                return 1;
            }
            else
            {
                if (obj is Person3)
                {
                    Person3 o = (Person3)obj;
                    return id - o.id;
                }
                throw new System.ArgumentException();
            }
        }

        public int CompareTo(Person3 p)
        {
            return p == null ? 1 : id - p.id;
        }

    }

    class Person4 : System.IComparable, System.IComparable<Person4>
    {
        private int idPart1;
        private string idPart2;
        private string name;
        private string surname;

        //...

        private int CompareTo0(Person4 p)
        {
            int v = idPart1 - p.idPart1;
            if (v == 0)
            {
                v = idPart2.CompareTo(p.idPart2);
            }
            return v;
        }

        public int CompareTo(object obj)
        {
            if (obj == null)
            {
                return 1;
            }
            else
            {
                if (obj is Person4)
                {
                    return CompareTo0((Person4)obj);
                }
                throw new System.ArgumentException();
            }
        }

        public int CompareTo(Person4 p)
        {
            if (p == null)
            {
                return 1;
            }
            else
            {
                return CompareTo0(p);
            }
        }

    }


En el primer y segundo ejemplo, se implementa la funcionalidad, utilizando la funcion CompareTo implementada por la clase string y por la estructura int32. Regresando el resultado de ejecutar dicha funcion. En el tercer ejemplo, se muestra, como se puede implementar utilizando la diferencia entre 2 enteros (es posible utilizar cualquier tipo entero, como un byte, short, int). Finalmente, en el cuarto ejemplo, se implementa la funcionalidad utilizando 2 propiedades, donde se compara la primer propiedad y en caso de igualdad, se regresa el resultado de comparar la segunda propiedad. Tambiem, en este ultimo ejemplo, define una funcion intermedia (CompareTo0), la cual hace la comparacion real y es llamada por las 2 implementaciones del CompareTo.

Como se observa. por razones de compatibilidad, es necesario implementar la funcionalidad de 2 interfaces, una utlizando Generics y otra sin pre-generics. En la version Generics, es necesario validar que el tipo de corresponde con la clase que implementa, en caso de no coincidir, hay que generar una excepcion del tipo ArgumentException. En el caso del null, la documentacion tambien especifica regresar 1 explicitamente. Final mente, en C#, como en C, se especifican valores validos cualquier entero positivo, negativo o cero (dentro de los rangos de int32)

Es recomendable, por cuestiones de simplicidad en la implementacion, incluir en la comparacion solo los valores que corresponden a la llave primaria, comparandolos, en orden de importancia, regresando el valor, del primero que resulte distinto (como se muestra en el ejemplo 4).

jueves, 28 de abril de 2011

Implementación la funcion compareTo de Comparable en java





La interface Comparable se emplea, para indicar si el objeto representado por this es menor, igual o mayor que el objeto que se pasa como parametro. A continuacion se presentan 3 ejemplos de clases que definen la funcion compareTo en base a una propiedad existente. Nota, en algunos paises existe el concepto de National Identification Number (CURP en México, RUN en Chile, DNI en España y Argentina o Tarjeta de Identidad en colombia, por ejemplo)


public class ExamplePerson1 implements Comparable<ExamplePerson1> {

 private String nin; // National Identification Number
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson1 o) {
  return this.nin.compareTo(o.nin);
 }

}

public class ExamplePerson2 implements Comparable<ExamplePerson2> {

 private Integer id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson2 o) {
  return this.id.compareTo(o.id);
 }

}

public class ExamplePerson3 implements Comparable<ExamplePerson3> {

 private int id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson3 o) {
  return this.id < o.id ? -1 : (this.id == o.id ? 0 : 1);
 }

}

En el ejemplo 1 y 2, se emplean propiedades de tipo String e Integer las cuales implementan la interface Comparable, por lo que solo se regresa el resultado de llamar la funcion compareTo de la propiedad respectiva. En el ejemplo 3 se utiliza una propiedad de tipo nativo y se muestra un ejemplo de como puede implementarse la funcion, utilizando las clases que envuelven a los tipos nativos (wrappers), evitando el uso de autoboxing y autounboxing.

En estos ejemplos, solo se emplea una propiedad, para implementar la funcionalidad. La recomendacion, es utilizar solo los atributos que forman la llave primaria (el concepto de base de datos). En caso de requerir utilizar mas de una propiedad, se incremanta el grado de complejidad. Como se muestra en el siguiente ejemplo.

public class ExamplePerson4 implements Comparable<ExamplePerson4> {

 private Integer idPart1;
 private Integer idPart2;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson4 o) {
  int result = this.idPart1.compareTo(o.idPart1);
  if(result == 0) {
   result = this.idPart2.compareTo(o.idPart2);
  }
  return result;
 }

}

Como se observa, si la comparacion de la primer propiedad es cero, hay que comparar la segunda propiedad y asi sucesivamente, hasta encontrar una distinta o ya no tener mas propiedades.

--- Actualizacion ---

En ocasiones, es necesario evitar que la comparacion genere una excepcion del tipo NullPointerException, cuando el parametro que recibe la funcion compareTo, es null. Para procesar el null, es necesario insertar un if para validar si es null, en caso de ser null, se regresa el valor necesario (normalmente 0), en caso contrario, se regresa el valor calculado, utilizando el algoritmo descrito anteriormente.

public class ExamplePerson5 implements Comparable<ExamplePerson5> {

 private Integer id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson5 o) {
  return o == null ? 0 : this.id.compareTo(o.id);
 }

}

Existe la posibilidad de implmentarlo utilizando un try-catch, en vez de utilizar un if. Si bien en apariencia no se ejecutan 2 operaciones, cuando se emplea el if (primero la comparacion contra null y posteriormente la evaluacion del if en si). En la practica no es optimo, dado que cuando se recibe un null, se genera una excepcion, que implicitamente es, la creacion del objeto excepcion, el llenado del stack (que en el caso de un programa muy sencillo son 3 o 4 elementos en el stack, pero en el caso de sistemas complejos, pueden ser mas de 50 elementos).

public class ExamplePerson6 implements Comparable<ExamplePerson6> {

 private Integer id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson6 o) {
  try {
   return this.id.compareTo(o.id);
  } catch (NullPointerException e) {
   return 0;
  }
 }

}

En conclusion, siempre es recomendable utilizar un if, si se necesita regresar un valor en caso de que el parametro sea null y evitar el uso de un try-catch.

miércoles, 27 de abril de 2011

Diferencia entre 2 fechas (años cumplidos) en java

Obtener los años cumplidos, a partir de una fecha en particular, tiene sus aplicaciones (validar mayoria de edad en las personas, verificar si aun aplica una garantia, a partir de la fecha de compra, etc)

La aproximacion, es la siguiente:


 1 import java.util.Calendar;
 2 import java.util.Date;
 3 
 4 public class DatesDiff {
 5 
 6  public static int calculateAge(Date bornDate) {
 7   Calendar cal = Calendar.getInstance(); // current date
 8   int currYear = cal.get(Calendar.YEAR);
 9   int currMonth = cal.get(Calendar.MONTH);
10   int currDay = cal.get(Calendar.DAY_OF_MONTH);
11   cal.setTime(bornDate); // now born date
12   int years = currYear - cal.get(Calendar.YEAR);
13   int bornMonth = cal.get(Calendar.MONTH);
14   if (bornMonth == currMonth) { // same month
15    return cal.get(Calendar.DAY_OF_MONTH) <= currDay ? years
16      : years - 1;
17   } else {
18    return bornMonth < currMonth ? years - 1 : years;
19   }
20  }
21 
22 }


Explicación:
07-07: Se obtiene la fecha actual, al obtener una instancia del objeto Calendar
08-10: Se obtienen lo valores del año, mes y dia actual (cache)
11-11: Se actualiza el objeto cal, con la fecha de nacimiento
12-12: Se calcula la diferencia de años
13-13: Se obtiene el año de nacimiento (cache)
14-14: Se verifica si el mes actual coincide con el mes de nacimiento
15-15:  Si los meses coinciden, se valida si el los dias coinciden, de ser este el caso, se regresan la diferencia de años, en caso contrario (aun no se cumple otro año) se regresa la diferencia menos uno.
18-18: Si los meses no coinciden, se regresala diferencia de los años menos 1 si el mes de nacimiento es menor que el mes actual y la diferencia de años, en caso contrario

En este codigo, se utiliza un solo objeto Calendar en vez de 2 objetos distintos. En vez de utilizar un objeto Calendar nuevo, obtenemos los valores que se emplearan a posteriormente en variables de tipo nativo, las cuales radican en el pila (stack), en vez de el  (heap).

Una optimizacion posible, es parametrizar la fecha actual, para el tema de internacionalizacion, con respecto a las zonas horarias, ya que se da la posibilidad que tomando en cuenta la fecha de la zona horaria de un pais distnto puede ser que se cumpla o no un año mas.