Categorías
Java

¿Cómo se imprime “Hello World”? – Un ciclo de vida completo

Publiqué esta pregunta en Stack Overflow hoy porque el ciclo de vida completo de un programa simple es interesante.

Seguiré actualizando la publicación usando respuestas / comentarios para hacerla más completa y corregir mi error. Seguramente votaré a favor de cualquier respuesta / comentario que tenga sentido. Por favor vote o publique una respuesta / comentario para ayudar. ¡Muchas gracias!

Enlace de pregunta: http://stackoverflow.com/q/16311867/127859

El siguiente es EL programa HelloWorld que todo el mundo conoce.

Está compilado en un código de bytes como el siguiente.

2

El código de bytes no se puede leer, pero podemos usar javap -classpath. -c HelloWorld para ver mnemónicos como los siguientes.

3

Luego se carga, vincula e inicializa en JVM.

4.

Dado que solo tiene un subproceso, supongamos que es el subproceso izquierdo en el siguiente área de datos en tiempo de ejecución de JVM.

5

JVM hace las instrucciones x86.

Los subprocesos de JVM son subprocesos a nivel de usuario, por lo que se correlacionarán con el kernel. En Ubuntu, es un mapeo uno a uno como el siguiente:

6

¿Cuál es la función del sistema operativo para este programa en particular?

JVM está encima de gllibc, syscalls. io driver in os será responsable de imprimir palabras en la consola. Las llamadas al sistema relacionadas con io deben implementarse en el subsistema io en os.

¿Qué sigue en Arquitectura?

Obtener instrucciones, decodificar, ejecutar, acceder a la memoria, volver a escribir en MIPS de 5 pasos.

7

Categorías
Java

Descarga de PDF de «Java simple»

Ultima versión:

Java simple (2015)

Versiones antiguas:

Enlace de descarga simple de Java (2013)
Enlace de descarga simple de Java (2014)

Categorías
Collections Java

Java – Ordenar mapa por valor

En Java, podemos usar el TreeMap class para ordenar un mapa por sus claves. Esta clase es muy útil de usar. Sin embargo, a veces necesitamos ordenar un mapa por sus valores. Cómo ordenar un mapa por sus valores es una de las preguntas más frecuentes de los programadores de Java. En esta publicación, desarrollaré la mejor manera de escribir dicho método.

1. Método ingenuo

La siguiente es una solución para ordenar un mapa de pares . Esto se usa a menudo para contar la frecuencia de las palabras.

import java.util.Comparator;
import java.util.HashMap;
import java.util.TreeMap;
 
public class SortMapByValue {
 
	public static void main(String[] args) {
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 10);
		map.put("b", 30);
		map.put("c", 50);
		map.put("d", 40);
		map.put("e", 20);
		System.out.println(map);
 
		TreeMap<String, Integer> sortedMap = sortMapByValue(map);  
		System.out.println(sortedMap);
	}
 
	public static TreeMap<String, Integer> sortMapByValue(HashMap<String, Integer> map){
		Comparator<String> comparator = new ValueComparator(map);
		//TreeMap is a map sorted by its keys. 
		//The comparator is used to sort the TreeMap by keys. 
		TreeMap<String, Integer> result = new TreeMap<String, Integer>(comparator);
		result.putAll(map);
		return result;
	}
}

Aquí está la implementación de la clase de comparador.

// a comparator that compares Strings
class ValueComparator implements Comparator<String>{
 
	HashMap<String, Integer> map = new HashMap<String, Integer>();
 
	public ValueComparator(HashMap<String, Integer> map){
		this.map.putAll(map);
	}
 
	@Override
	public int compare(String s1, String s2) {
		if(map.get(s1) >= map.get(s2)){
			return -1;
		}else{
			return 1;
		}	
	}
}

En esta solución, usamos un TreeMap para ordenar el mapa. Al crear el TreeMap, le damos un comparador. El comparador acepta cadenas y compara los valores asociados de la clave de cadena dada en el mapa.

El método funciona bien, pero solo funciona para ordenar pares de cadenas y enteros. Si queremos ordenar un mapa con otros tipos de claves y valores, es necesario reescribirlo. Por tanto, se prefiere una solución más general.

2. Solución más general

Podemos ignorar el tipo genérico y hacer que los métodos funcionen para cualquier tipo como el siguiente.

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
 
public class Solution {
	public static void main(String[] args) {
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 10);
		map.put("b", 30);
		map.put("c", 50);
		map.put("d", 40);
		map.put("e", 20);
		System.out.println(map);
 
		Map sortedMap = sortByValue(map);
		System.out.println(sortedMap);
	}
 
	public static Map sortByValue(Map unsortedMap) {
		Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
		sortedMap.putAll(unsortedMap);
		return sortedMap;
	}
 
}
 
class ValueComparator implements Comparator {
	Map map;
 
	public ValueComparator(Map map) {
		this.map = map;
	}
 
	public int compare(Object keyA, Object keyB) {
		Comparable valueA = (Comparable) map.get(keyA);
		Comparable valueB = (Comparable) map.get(keyB);
		return valueB.compareTo(valueA);
	}
}

La solución no es de tipo seguro, necesitamos una solución general y de tipo seguro.

3. Uso de tipos genéricos

public class SortMapByValue {
 
	public static void main(String[] args) {
		// <String, Integer> Map
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 10);
		map.put("b", 30);
		map.put("c", 50);
		map.put("d", 40);
		map.put("e", 20);
		System.out.println(map);
 
 
		Comparator<String> comparator = new ValueComparator<String, Integer>(map);
		TreeMap<String, Integer> result = new TreeMap<String, Integer>(comparator);
		result.putAll(map);
 
		System.out.println(result);
 
		// <Integer, Integer> Map
 
		HashMap<Integer, Integer> map2 = new HashMap<Integer, Integer>();
		map2.put(1, 10);
		map2.put(2, 30);
		map2.put(3, 50);
		map2.put(4, 40);
		map2.put(5, 20);
		System.out.println(map2);
 
		Comparator<Integer> comparator2 = new ValueComparator<Integer, Integer>(map2);
		TreeMap<Integer, Integer> result2 = new TreeMap<Integer, Integer>(comparator2);
		result2.putAll(map2);
 
		System.out.println(result2);
 
	}
 
}
// a comparator using generic type
class ValueComparator<K, V extends Comparable<V>> implements Comparator<K>{
 
	HashMap<K, V> map = new HashMap<K, V>();
 
	public ValueComparator(HashMap<K, V> map){
		this.map.putAll(map);
	}
 
	@Override
	public int compare(K s1, K s2) {
		return -map.get(s1).compareTo(map.get(s2));//descending order	
	}
}

4. Otra forma de utilizar tipos genéricos

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
	List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
	Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
		@Override
		public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
			return (e1.getValue()).compareTo(e2.getValue());
		}
	});
 
	Map<K, V> result = new LinkedHashMap<>();
	for (Map.Entry<K, V> entry : list) {
		result.put(entry.getKey(), entry.getValue());
	}
 
	return result;
}

5. Simple

Cuando necesite una solución simple, la clasificación se puede hacer en una línea.

LinkedList<Map.Entry<String, Integer>> list = new LinkedList<>(counter.entrySet());
Comparator<Map.Entry<String, Integer>> comparator = Comparator.comparing(Map.Entry::getValue);
Collections.sort(list, comparator.reversed());

Categorías
Basics Diagram Java

¿Por qué String es inmutable en Java?

String es inmutable en Java. Una clase inmutable es simplemente una clase cuyas instancias no se pueden modificar. Toda la información de una instancia se inicializa cuando se crea la instancia y la información no se puede modificar. Hay muchas ventajas de las clases inmutables. Este artículo resume por qué String está diseñado para ser inmutable. Este artículo ilustra el concepto de inmutabilidad en la perspectiva de la memoria, la sincronización y las estructuras de datos.

1. Requisito de la agrupación de cadenas

El grupo de cuerdas (grupo interno de cuerdas) es un área de almacenamiento especial en el área de métodos. Cuando se crea una cadena y si la cadena ya existe en el grupo, se devolverá la referencia de la cadena existente, en lugar de crear un nuevo objeto.

El siguiente código creará solo un objeto de cadena en el montón.

String string1 = "abcd";
String string2 = "abcd";

Así es como se ve:

Si una cadena es mutable, cambiar la cadena con una referencia conducirá a un valor incorrecto para las otras referencias.

2. Almacenamiento en caché de Hashcode

El código hash de una cadena se usa con frecuencia en Java. Por ejemplo, en un HashMap o HashSet. Ser inmutable garantiza que el hashcode siempre será el mismo para que se pueda cobrar sin preocuparse por los cambios, es decir, no es necesario calcular el hashcode cada vez que se utiliza. Esto es más eficiente.

En la clase String, tiene el siguiente código:

private int hash;//this is used to cache hash code.

3. Facilitar el uso de otros objetos

Para hacer esto concreto, considere el siguiente programa:

HashSet<String> set = new HashSet<String>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));
 
for(String a: set)
	a.value = "a";

En este ejemplo, si String es mutable, su valor se puede cambiar, lo que violaría el diseño del conjunto (el conjunto contiene elementos no duplicados). Por supuesto, el ejemplo anterior es solo para fines de demostración y no hay value campo en una clase de cadena real.

4. Seguridad

String se usa ampliamente como parámetro para muchas clases de Java, por ejemplo, conexión de red, apertura de archivos, etc. Si String no fuera inmutable, se cambiaría una conexión o un archivo y esto puede conducir a una seria amenaza a la seguridad. El método pensó que se estaba conectando a una máquina, pero no fue así. Las cadenas mutables también podrían causar un problema de seguridad en Reflection, ya que los parámetros son cadenas.

Aquí hay un ejemplo de código:

boolean connect(string s){
    if (!isSecure(s)) { 
throw new SecurityException(); 
}
    //here will cause problem, if s is changed before this by using other references.    
    causeProblem(s);
}

5. Los objetos inmutables son naturalmente seguros para subprocesos

Debido a que los objetos inmutables no se pueden cambiar, se pueden compartir libremente entre varios subprocesos. Esto elimina los requisitos de sincronización.

En resumen, String está diseñado para ser inmutable por razones de eficiencia y seguridad. Esta es también la razón por la que se prefieren las clases inmutables en muchos casos en general.

Categorías
Basics Diagram Java Versus

La cadena se pasa por «referencia» en Java

Esta es una pregunta clásica de Java. Se han hecho muchas preguntas similares sobre stackoverflow y hay muchas respuestas incorrectas / incompletas. La pregunta es simple si no piensas demasiado. Pero podría ser muy confuso si lo piensa más.

1. Un fragmento de código interesante y confuso

public static void main(String[] args) {
	String x = new String("ab");
	change(x);
	System.out.println(x);
}
 
public static void change(String x) {
	x = "cd";
}

Imprime «ab».

En C ++, el código es el siguiente:

void change(string &x) {
    x = "cd";
}
 
int main(){
    string x = "ab";
    change(x);
    cout << x << endl;
}

imprime «cd».

2. Preguntas frecuentes confusas

X almacena la referencia que apunta a la cadena «ab» en el montón. Así que cuando X se pasa como un parámetro al cambio() método, todavía apunta al «ab» en el montón como el siguiente:

Como java se pasa por valor, el valor de X es la referencia a «ab». Cuando el metodo cambio() se invoca, crea un nuevo objeto «cd» y X ahora apunta a «cd» como el siguiente:

cadena-paso-por-referencia 2

Parece una explicación bastante razonable. Tienen claro que Java siempre se pasa por valor. Pero, ¿qué pasa aquí?

3. ¿Qué hace realmente el código?

La explicación anterior tiene varios errores. Para entender esto fácilmente, es una buena idea recorrer brevemente todo el proceso.

Cuando se crea la cadena «ab», Java asigna la cantidad de memoria necesaria para almacenar el objeto de cadena. Luego, el objeto se asigna a la variable X, a la variable se le asigna realmente una referencia al objeto. Esta referencia es la dirección de la ubicación de la memoria donde se almacena el objeto.

La variable X contiene una referencia al objeto de cadena. X ¡no es una referencia en sí misma! Es una variable que almacena una referencia (dirección de memoria).

Java es SÓLO paso por valor. Cuando se pasa x al método change (), se pasa una copia del valor de x (una referencia). El método cambio() crea otro objeto «cd» y tiene una referencia diferente. Es la variable x la que cambia su referencia (a «cd»), no la referencia en sí.

El siguiente diagrama muestra lo que realmente hace.
cadena-paso-por-referencia 3

4. La explicación incorrecta

El problema que surge del primer fragmento de código no está relacionado con la inmutabilidad de las cadenas. Incluso si String se reemplaza con StringBuilder, el resultado sigue siendo el mismo. El punto clave es que la variable almacena la referencia, ¡pero no es la referencia en sí misma!

5. Solución a este problema

Si realmente necesitamos cambiar el valor del objeto. En primer lugar, el objeto debe ser modificable, por ejemplo, StringBuilder. En segundo lugar, debemos asegurarnos de que no haya ningún objeto nuevo creado y asignado a la variable de parámetro, porque Java solo se pasa por valor.

public static void main(String[] args) {
	StringBuilder x = new StringBuilder("ab");
	change(x);
	System.out.println(x);
}
 
public static void change(StringBuilder x) {
	x.delete(0, 2).append("cd");
}

Categorías
Basics Java Top 10

Los 8 mejores diagramas para comprender Java

Un diagrama a veces vale 1000 palabras. Los siguientes diagramas son de tutoriales de Java en Program Creek, han recibido la mayor cantidad de votos hasta ahora. Con suerte, pueden ayudarlo a revisar lo que ya sabe. Si el problema no está claro en el diagrama en sí, es posible que desee ir a cada artículo para profundizar.

1. Inmutabilidad de las cuerdas

El siguiente diagrama muestra lo que sucede con el siguiente código:

String s = "abcd";
s = s.concat("ef");

2. El contrato equals () y hashCode ()

HashCode está diseñado para mejorar el rendimiento. El contrato entre equals () y hasCode () es que:
1. Si dos objetos son iguales, entonces deben tener el mismo código hash.
2. Si dos objetos tienen el mismo código hash, pueden ser iguales o no.

java-hashcode

3. Jerarquía de clases de excepción de Java

De color rojo son excepciones marcadas que deben ser capturadas o declaradas en la cláusula throws del método.

Diagrama de jerarquía de excepciones

4. Jerarquía de clases de colecciones

Tenga en cuenta la diferencia entre colecciones y colección.


5. Sincronización de Java

El mecanismo de sincronización de Java se puede ilustrar mediante una analogía con un edificio.

6. Alias

La creación de alias significa que hay varios alias en una ubicación que se pueden actualizar, y estos alias tienen diferentes tipos.

Alias ​​de Java

7. Apilar y amontonar

Este diagrama muestra dónde están los métodos y los objetos en la memoria en tiempo de ejecución.

Java-matriz-en-memoria

8. Áreas de datos en tiempo de ejecución de JVM

Este diagrama muestra áreas generales de datos en tiempo de ejecución de JVM.

Área de datos de tiempo de ejecución de JVM

Categorías
Basics Classes & Interfaces Collections Java Top 10

Las 9 preguntas principales sobre Java Maps

En general, Mapa es una estructura de datos que consta de un conjunto de valor clave pares, y cada clave solo puede aparecer una vez en el mapa. Esta publicación resume las 9 preguntas más frecuentes sobre cómo usar Java Mapa y sus clases implementadas. En aras de la simplicidad, usaré genéricos en ejemplos. Por lo tanto, solo escribiré Map en lugar de específico Map. Pero siempre puede asumir que tanto el K y V son comparables, lo que significa K extends Comparable y V extends Comparable.


0. Convertir un mapa en una lista

En Java, Mapa La interfaz proporciona tres vistas de colección: conjunto de claves, conjunto de valores y conjunto de valores-clave. Todos ellos se pueden convertir a Lista usando un constructor o añadir todo() método. El siguiente fragmento de código muestra cómo construir un Lista de arreglo de un mapa.

// key list
List keyList = new ArrayList(map.keySet());
// value list
List valueList = new ArrayList(map.values());
// key-value list
List entryList = new ArrayList(map.entrySet());

1. Iterar sobre un mapa

La iteración sobre cada par de pares clave-valor es la operación más básica para recorrer un mapa. En Java, dicho par se almacena en la entrada del mapa llamada Entrada del mapa. Mapa.entrySet () devuelve un conjunto clave-valor, por lo tanto, la forma más eficiente de revisar cada entrada de un mapa es

for(Entry entry: map.entrySet()) {
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

El iterador también se puede utilizar, especialmente antes de JDK 1.5

Iterator itr = map.entrySet().iterator();
while(itr.hasNext()) {
  Entry entry = itr.next();
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

2. Ordenar un mapa en las claves

Ordenar un mapa en las claves es otra operación frecuente. Una forma es poner Entrada del mapa en una lista y ordenarlo usando un comparador que ordena el valor.

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {
 
  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getKey().compareTo(e2.getKey());
  }
 
});

La otra forma es usar SortedMap, que además proporciona un orden total en sus claves. Por lo tanto, todas las claves deben implementar Comparable o ser aceptado por el comparador.

Una clase de implementación de SortedMap es TreeMap. Su constructor puede aceptar un comparador. El siguiente código muestra cómo transformar un mapa general en un mapa ordenado.

SortedMap sortedMap = new TreeMap(new Comparator() {
 
  @Override
  public int compare(K k1, K k2) {
    return k1.compareTo(k2);
  }
 
});
sortedMap.putAll(map);

3. Ordene un mapa según los valores

Poner el mapa en una lista y ordenarlo también funciona en este caso, pero necesitamos comparar Entrada.getValue () esta vez. El siguiente código es casi el mismo que antes.

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {
 
  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getValue().compareTo(e2.getValue());
  }
 
});

Todavía podemos usar un mapa ordenado para esta pregunta, pero solo si los valores también son únicos. Bajo tal condición, puede invertir el par clave = valor a valor = clave. Esta solución tiene una limitación muy fuerte, por lo tanto, no la recomiendo realmente.

4. Inicialice un mapa estático / inmutable

Cuando espera que un mapa permanezca constante, es una buena práctica copiarlo en un mapa inmutable. Estas técnicas de programación defensiva le ayudarán a crear mapas de subprocesos no solo seguros para su uso, sino también seguros.

Para inicializar un mapa estático / inmutable, podemos usar un inicializador estático (como a continuación). El problema de este código es que, aunque mapa se declara como final estática, todavía podemos operarlo después de la inicialización, como Test.map.put(3,"three");. Por tanto, no es realmente inmutable. Para crear un mapa inmutable usando un inicializador estático, necesitamos una clase anónima adicional y la copiamos en un mapa no modificable en el último paso de la inicialización. Consulte la segunda parte del código. Entonces, un UnsupportedOperationException será arrojado si corres Test.map.put(3,"three");.

public class Test {
 
  private static final Map map;
  static {
    map = new HashMap();
    map.put(1, "one");
    map.put(2, "two");
  }
}
public class Test {
 
  private static final Map map;
  static {
    Map aMap = new HashMap();
    aMap.put(1, "one");
    aMap.put(2, "two");
    map = Collections.unmodifiableMap(aMap);
  }
}

Guayaba las bibliotecas también admiten diferentes formas de intilizar una colección estática e inmutable. Para obtener más información sobre los beneficios de las utilidades de recolección inmutables de Guava, consulte Colecciones inmutables explicadas en la guía del usuario de Guava.

5. Diferencia entre HashMap, TreeMap y Hashtable

Hay tres implementaciones principales de Mapa interfaz en Java: HashMap, TreeMap, y Tabla de picadillo. Las diferencias más importantes incluyen:

  1. El orden de iteración. HashMap y Tabla de picadillo no garantice el orden del mapa; en particular, no garantizan que el pedido se mantenga constante en el tiempo. Pero TreeMap iterará todas las entradas según el «orden natural» de las claves o mediante un comparador.
  2. valor clave permiso. HashMap permite nulo clave y nulo valores (solo se permite una clave nula porque no se permiten dos claves iguales). Tabla de picadillo no permite nulo clave o nulo valores. Si TreeMap utiliza ordenamiento natural o su comparador no permite nulo claves, se lanzará una excepción.
  3. Sincronizado. Solo Tabla de picadillo está sincronizado, otros no. Por lo tanto, «si no se necesita una implementación segura para subprocesos, se recomienda utilizar HashMap en lugar de Tabla de picadillo. «

Una comparación más completa es

                 | HashMap | Hashtable | TreeMap
-------------------------------------------------------
iteration order  | no      | no        | yes
null key-value   | yes-yes | no-no   | no-yes
synchronized     | no      | yes       | no
time performance | O(1)    | O(1)      | O(log n)
implementation   | buckets | buckets   | red-black tree

Lea más sobre HashMap frente a TreeMap frente a Hashtable frente a LinkedHashMap.

6. Un mapa con vista / búsqueda inversa

A veces, necesitamos un conjunto de pares clave-clave, lo que significa que los valores del mapa son únicos, así como las claves (mapa uno a uno). Esta restricción permite crear una «vista / búsqueda inversa» de un mapa. Entonces podemos buscar una clave por su valor. Tal estructura de datos se llama mapa bidireccional, que lamentablemente no es compatible con JDK.

Tanto Apache Common Collections como Guava proporcionan la implementación de un mapa bidireccional, llamado BidiMap y BiMap, respectivamente. Ambos aplican la restricción de que existe una relación 1: 1 entre claves y valores.

7. Copia superficial de un mapa

La mayor parte de la implementación de un mapa en Java, si no todas, proporciona un constructor de copia de otro mapa. Pero el procedimiento de copia es no sincronizado. Eso significa que cuando un hilo copia un mapa, otro puede modificarlo estructuralmente. Para[evitarunacopiaaccidentalnosincronizadasedebeusar[preventaccidentalunsynchronizedcopyoneshoulduseColecciones.SynchronizedMap () por adelantado.

Map copiedMap = Collections.synchronizedMap(map);

Otra forma interesante de copia superficial es mediante el uso de clon() método. sin embargo lo és NO incluso recomendado por el diseñador del marco de la colección de Java, Josh Bloch. En una conversación sobre «Copiar constructor versus clonación«, él dijo

A menudo proporciono un método de clonación pública en clases concretas porque la gente lo espera. … Es una pena que Cloneable esté roto, pero sucede. … Cloneable es un punto débil, y creo que la gente debería ser consciente de sus limitaciones.

Por esta razón, ni siquiera te diré cómo usar clon() método para copiar un mapa.

8. Crea un mapa vacío

Si el mapa es inmutable, use

map = Collections.emptyMap();

De lo contrario, use cualquier implementación. Por ejemplo

map = new HashMap();

EL FIN

Categorías
Java Regular Expressions Top 10

Las 10 preguntas principales para la expresión regular de Java

Esta publicación resume las principales preguntas formuladas sobre las expresiones regulares de Java. Como se preguntan con más frecuencia, es posible que también sean muy útiles.

1. ¿Cómo extraer números de una cadena?

Una pregunta común al usar expresiones regulares es extraer todos los números en una matriz de enteros.

En Java, d significa un rango de dígitos (0-9). Usar las clases predefinidas siempre que sea posible hará que su código sea más fácil de leer y eliminará los errores introducidos por clases de caracteres mal formadas. Por favor refiérase a Clases de caracteres predefinidas para más detalles. Tenga en cuenta la primera barra invertida en d. Si está utilizando una construcción de escape dentro de un literal de cadena, debe preceder la barra invertida con otra barra invertida para que la cadena se compile. Es por eso que necesitamos usar d.

List<Integer> numbers = new LinkedList<Integer>();
Pattern p = Pattern.compile("d+");
Matcher m = p.matcher(str); 
while (m.find()) {
  numbers.add(Integer.parseInt(m.group()));
}

2. ¿Cómo dividir Java String por líneas nuevas?

Hay al menos tres formas diferentes de ingresar un carácter de nueva línea, dependiendo del sistema operativo en el que esté trabajando.

r represents CR (Carriage Return), which is used in Unix
n means LF (Line Feed), used in Mac OS
rn means CR + LF, used in Windows

Por lo tanto, la forma más sencilla de dividir una cadena en nuevas líneas es

String lines[] = String.split("r?n");

Pero si no quieres líneas vacías, puedes usar, que también es mi forma favorita:

String.split("[rn]+")

Una forma más robusta, que es realmente independiente del sistema, es la siguiente. Pero recuerde, seguirá obteniendo líneas vacías si dos caracteres de nueva línea se colocan uno al lado del otro.

String.split(System.getProperty("line.separator"));

3. Importancia de Pattern.compile ()

Una expresión regular, especificada como una cadena, primero debe compilarse en una instancia de Patrón clase. Patrón.compilar() El método es la única forma de crear una instancia de objeto. Por tanto, una secuencia de invocación típica es

Pattern p = Pattern.compile("a*b");
Matcher matcher = p.matcher("aaaaab");
assert matcher.matches() == true;

Esencialmente, Patrón.compilar() se utiliza para transformar una expresión regular en una máquina de estados finitos (ver Compiladores: principios, técnicas y herramientas (segunda edición)). Pero todos los estados involucrados en la realización de un partido residen en el emparejador. De esta manera, el Patrón pag se puede reutilizar. Y muchos comparadores pueden compartir el mismo patrón.

Matcher anotherMatcher = p.matcher("aab");
assert anotherMatcher.matches() == true;

Patrón.partidos() El método se define como una conveniencia para cuando una expresión regular se usa solo una vez. Este método todavía usa compilar() para obtener la instancia de un Patrón implícitamente y coincide con una cadena. Por lo tanto,

boolean b = Pattern.matches("a*b", "aaaaab");

es equivalente al primer código anterior, aunque para coincidencias repetidas es menos eficiente ya que no permite reutilizar el patrón compilado.

4. ¿Cómo escapar del texto para una expresión regular?

En general, la expresión regular usa «» para escapar de las construcciones, pero es doloroso preceder la barra invertida con otra barra invertida para que la cadena de Java se compile. Hay otra forma para que los usuarios pasen literales de cadena al Patrón, como «$ 5». En lugar de escribir $5 o [$]5, podemos escribir

Pattern.quote("$5");

5. ¿Por qué String.split () necesita un delimitador de tubería para escapar?

Cuerda.separar() divide una cadena alrededor de coincidencias de la expresión regular dada. La expresión Java admite caracteres especiales que afectan la forma en que se hace coincidir un patrón, que se llama metacarácter. | es un metacarácter que se utiliza para hacer coincidir una sola expresión regular entre varias expresiones regulares posibles. Por ejemplo, A|B significa ya sea A o B. Por favor refiérase a Alternancia con el símbolo de barra vertical o tubería para más detalles. Por lo tanto, para usar | como literal, debe escapar agregando delante de ella, como |.

6. ¿Cómo podemos igualar unnorteBnorte con Java regex?

Este es el idioma de todas las cadenas no vacías que constan de un número de aseguido de un número igual de bes como ab, aabb, y aaabbb. Se puede demostrar que este lenguaje es gramática libre de contexto S → aSb | ab, y por lo tanto un lenguaje no regular.

Sin embargo, las implementaciones de expresiones regulares de Java pueden reconocer más que solo lenguajes regulares. Es decir, no son «regulares» según la definición de la teoría formal del lenguaje. Utilizando mirar hacia el futuro y coincidencia de autorreferencia lo logrará. Aquí daré primero la expresión regular final y luego la explicaré un poco. Para una explicación completa, lo recomendaría leer ¿Cómo podemos hacer coincidir un ^ nb ^ n con Java regex?.

Pattern p = Pattern.compile("(?x)(?:a(?= a*(1?+b)))+1");
// true
System.out.println(p.matcher("aaabbb").matches());
// false
System.out.println(p.matcher("aaaabbb").matches());
// false
System.out.println(p.matcher("aaabbbb").matches());
// false
System.out.println(p.matcher("caaabbb").matches());

En lugar de explicar la sintaxis de esta compleja expresión regular, prefiero decir un poco cómo funciona.

  1. En la primera iteración, se detiene en la primera a luego mira hacia adelante (después de omitir algunos as usando a*) si hay un b. Esto se logró utilizando (?:a(?= a*(1?+b))). Si coincide, 1, la coincidencia de autorreferencia, coincidirá con los elementos entre paréntesis muy internos, que es un solo b en la primera iteración.
  2. En la segunda iteración, la expresión se detendrá en la segunda a, luego mira hacia adelante (de nuevo saltando as) para ver si habrá b. Pero esta vez, 1+b es en realidad equivalente a bb, por lo tanto dos bs tienen que coincidir. Si es así, 1 será cambiado a bb después de la segunda iteración.
  3. En el norteth iteración, la expresión se detiene en el norteth a y mira si hay norte bs adelante.

De esta forma, la expresión puede contar el número de asy coincidir si el número de bs seguido de a es igual.

7. ¿Cómo reemplazar 2 o más espacios con un solo espacio en una cadena y eliminar solo los espacios iniciales?

Cuerda.reemplaza todo() reemplaza cada subcadena que coincide con la expresión regular dada con el reemplazo dado. «2 o más espacios» se pueden expresar mediante una expresión regular [ ]+. Por lo tanto, el siguiente código funcionará. Tenga en cuenta que, en última instancia, la solución no eliminará todos los espacios en blanco iniciales y finales. Si desea que se eliminen, puede utilizar Cuerda.podar() En la tuberia.

String line = "  aa bbbbb   ccc     d  ";
// " aa bbbbb ccc d "
System.out.println(line.replaceAll("[s]+", " "));

8. ¿Cómo determinar si un número es primo con expresión regular?

public static void main(String[] args) {
  // false
  System.out.println(prime(1));
  // true
  System.out.println(prime(2));
  // true
  System.out.println(prime(3));
  // true
  System.out.println(prime(5));
  // false
  System.out.println(prime(8));
  // true
  System.out.println(prime(13));
  // false
  System.out.println(prime(14));
  // false
  System.out.println(prime(15));
}
 
public static boolean prime(int n) {
  return !new String(new char[n]).matches(".?|(..+?)1+");
}

La función primero genera norte número de caracteres e intenta ver si esa cadena coincide .?|(..+?)1+. Si es primo, la expresión devolverá falso y el ! invertirá el resultado.

La primera parte .? solo intenta asegurarse de que 1 no sea un cebador. La parte mágica es la segunda parte donde se usa la referencia inversa. (..+?)1+ primer intento de coincidencias norte longitud de los caracteres, luego repítalo varias veces 1+.

Por definición, un número primo es un número natural mayor que 1 que no tiene divisores positivos distintos de 1 y él mismo. Eso significa que si a = n * m luego a no es un primo. Nuevo Méjico se puede explicar con más detalle «repetir norte metro veces «, y eso es exactamente lo que hace la expresión regular: coincide norte longitud de caracteres usando (..+?), luego repítelo metro veces usando 1+. Por lo tanto, si el patrón coincide, el número no es primo, de lo contrario lo es. Recuerda que ! invertirá el resultado.

9. ¿Cómo dividir una cadena separada por comas pero ignorando las comas entre comillas?

Ha llegado al punto en que las expresiones regulares se rompen. Es mejor y más ordenado escribir un divisor simple y maneja casos especiales como lo desee.

Alternativamente, puede imitar el funcionamiento de una máquina de estados finitos, utilizando una instrucción switch o if-else. Se adjunta un fragmento de código.

public static void main(String[] args) {
  String line = "aaa,bbb,"c,c",dd;dd,"e,e";
  List<String> toks = splitComma(line);
  for (String t : toks) {
    System.out.println("> " + t);
  }
}
 
private static List<String> splitComma(String str) {
  int start = 0;
  List<String> toks = new ArrayList<String>();
  boolean withinQuote = false;
  for (int end = 0; end < str.length(); end++) {
    char c = str.charAt(end);
    switch(c) {
    case ',':
      if (!withinQuote) {
        toks.add(str.substring(start, end));
        start = end + 1;
      }
      break;
    case '"':
      withinQuote = !withinQuote;
      break;
    }
  }
  if (start < str.length()) {
    toks.add(str.substring(start));
  }
  return toks;
}

10. Cómo usar backreferences en expresiones regulares de Java

Las referencias inversas son otra característica útil en la expresión regular de Java.

Categorías
Collections Java

Contador eficiente en Java

A menudo, es posible que necesite un contador para comprender la frecuencia de algo (por ejemplo, palabras) de una base de datos o un archivo de texto. Un contador se puede implementar fácilmente usando un HashMap en Java. Este artículo compara diferentes enfoques para implementar un contador. Finalmente, se concluirá uno eficiente.

ACTUALIZACIÓN: Eche un vistazo al contador de Java 8, escribir un contador son solo 2 líneas simples ahora.

1. El Ingenuo Encimera

Ingenuamente, se puede implementar de la siguiente manera:

String s = "one two three two three three";
String[] sArr = s.split(" ");
 
//naive approach	 
HashMap<String, Integer> counter = new HashMap<String, Integer>();
 
for (String a : sArr) {
	if (counter.containsKey(a)) {
		int oldValue = counter.get(a);
		counter.put(a, oldValue + 1);
	} else {
		counter.put(a, 1);
	}
}

En cada bucle, verifica si la clave existe o no. Si es así, incremente el valor anterior en 1, si no, establézcalo en 1. Este enfoque es simple y directo, pero no es el enfoque más eficiente. Este método se considera menos eficaz por las siguientes razones:

  • containsKey (), get () se llaman dos veces cuando ya existe una clave. Eso significa buscar en el mapa dos veces.
  • Dado que Integer es inmutable, cada bucle creará uno nuevo para incrementar el valor anterior

2. El Mejor Encimera

Naturalmente, queremos un entero mutable para evitar la creación de muchos objetos Integer. Una clase de entero mutable se puede definir de la siguiente manera:

class MutableInteger {
 
	private int val;
 
	public MutableInteger(int val) {
		this.val = val;
	}
 
	public int get() {
		return val;
	}
 
	public void set(int val) {
		this.val = val;
	}
 
	//used to print value convinently
	public String toString(){
		return Integer.toString(val);
	}
}

Y el contador se mejora y cambia a lo siguiente:

HashMap<String, MutableInteger> newCounter = new HashMap<String, MutableInteger>();	
 
for (String a : sArr) {
	if (newCounter.containsKey(a)) {
		MutableInteger oldValue = newCounter.get(a);
		oldValue.set(oldValue.get() + 1);
	} else {
		newCounter.put(a, new MutableInteger(1));
	}
}

Esto parece mejor porque ya no requiere la creación de muchos objetos Integer. Sin embargo, la búsqueda sigue siendo dos veces en cada bucle si existe una clave.

3. El Eficiente Encimera

El método HashMap.put (clave, valor) devuelve el valor actual de la clave. ¡Esto es útil, porque podemos usar la referencia del valor anterior para actualizar el valor sin buscar una vez más!

HashMap<String, MutableInteger> efficientCounter = new HashMap<String, MutableInteger>();
 
for (String a : sArr) {
	MutableInteger initValue = new MutableInteger(1);
	MutableInteger oldValue = efficientCounter.put(a, initValue);
 
	if(oldValue != null){
		initValue.set(oldValue.get() + 1);
	}
}

4. Diferencia de rendimiento

Para probar el rendimiento de los tres enfoques diferentes, se utiliza el siguiente código. La prueba de rendimiento se realiza 1 millón de veces. Los resultados brutos son los siguientes:

Naive Approach :  222796000
Better Approach:  117283000
Efficient Approach:  96374000

La diferencia es significativa: 223 frente a 117 frente a 96. Existe una gran diferencia entre Ingenuo y Mejor, lo que indica que crear objetos es caro.

String s = "one two three two three three";
String[] sArr = s.split(" ");
 
long startTime = 0;
long endTime = 0;
long duration = 0;
 
// naive approach
startTime = System.nanoTime();
HashMap<String, Integer> counter = new HashMap<String, Integer>();
 
for (int i = 0; i < 1000000; i++)
	for (String a : sArr) {
		if (counter.containsKey(a)) {
			int oldValue = counter.get(a);
			counter.put(a, oldValue + 1);
		} else {
			counter.put(a, 1);
		}
	}
 
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("Naive Approach :  " + duration);
 
// better approach
startTime = System.nanoTime();
HashMap<String, MutableInteger> newCounter = new HashMap<String, MutableInteger>();
 
for (int i = 0; i < 1000000; i++)
	for (String a : sArr) {
		if (newCounter.containsKey(a)) {
			MutableInteger oldValue = newCounter.get(a);
			oldValue.set(oldValue.get() + 1);
		} else {
			newCounter.put(a, new MutableInteger(1));
		}
	}
 
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("Better Approach:  " + duration);
 
// efficient approach
startTime = System.nanoTime();
 
HashMap<String, MutableInteger> efficientCounter = new HashMap<String, MutableInteger>();
 
for (int i = 0; i < 1000000; i++)
	for (String a : sArr) {
		MutableInteger initValue = new MutableInteger(1);
		MutableInteger oldValue = efficientCounter.put(a, initValue);
 
		if (oldValue != null) {
			initValue.set(oldValue.get() + 1);
		}
	}
 
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("Efficient Approach:  " + duration);

Cuando usa un contador, probablemente también necesite una función para ordenar el mapa por valor. Puede consultar el método de uso frecuente de HashMap.

5. Soluciones de Keith

Se agregaron un par de pruebas:
1) Refactorizado «mejor enfoque» para simplemente llamar a get en lugar de containsKey. Por lo general, los elementos que desea están en el HashMap, por lo que se reduce de dos búsquedas a una.
2) Se agregó una prueba con AtomicInteger, que Michal mencionó.
3) En comparación con la matriz int singleton, que usa menos memoria según http://amzn.com/0748614079

Ejecuté el programa de prueba 3 veces y tomé el mínimo para eliminar la variación de otros programas. Tenga en cuenta que no puede hacer esto dentro del programa o los resultados se ven demasiado afectados, probablemente debido a gc.

Naive: 201716122
Better Approach: 112259166
Efficient Approach: 93066471
Better Approach (without containsKey): 69578496
Better Approach (without containsKey, with AtomicInteger): 94313287
Better Approach (without containsKey, with int[]): 65877234

Mejor enfoque (sin containsKey):

HashMap<String, MutableInteger> efficientCounter2 = new HashMap<String, MutableInteger>();
for (int i = 0; i < NUM_ITERATIONS; i++) {
	for (String a : sArr) {
		MutableInteger value = efficientCounter2.get(a);
 
		if (value != null) {
			value.set(value.get() + 1);
		} else {
			efficientCounter2.put(a, new MutableInteger(1));
		}
	}
}

Mejor enfoque (sin containsKey, con AtomicInteger):

HashMap<String, AtomicInteger> atomicCounter = new HashMap<String, AtomicInteger>();
for (int i = 0; i < NUM_ITERATIONS; i++) {
	for (String a : sArr) {
		AtomicInteger value = atomicCounter.get(a);
 
		if (value != null) {
			value.incrementAndGet();
		} else {
			atomicCounter.put(a, new AtomicInteger(1));
		}
	}
}

Mejor enfoque (sin containsKey, con int[]):

HashMap<String, int[]> intCounter = new HashMap<String, int[]>();
for (int i = 0; i < NUM_ITERATIONS; i++) {
	for (String a : sArr) {
		int[] valueWrapper = intCounter.get(a);
 
		if (valueWrapper == null) {
			intCounter.put(a, new int[] { 1 });
		} else {
			valueWrapper[0]++;
		}
	}
}

Probablemente, el MultiSet de Guava sea aún más rápido.

6. Conclusión

El ganador es el último que usa matrices int.

Referencias:
1. La forma más eficiente de incrementar un valor de Map en Java.
2. HashMap.put ()

Categorías
Java Java 8 Stream

Concat Streams en Java 8

A menudo, es posible que deba realizar una concatenación o fusionar dos flujos. En la clase Stream, hay un método estático concat () que se puede usar para este propósito.

Fusionar dos corrientes

String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "e", "f", "g" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
 
Stream<String> stream3 = Stream.concat(stream1, stream2);
String[] arr = stream3.toArray(String[]::new);
System.out.println(Arrays.toString(arr));

Producción:

[a, b, c, d, e, f, g]

Fusionar una secuencia filtrada con otra

Si necesita filtrar una secuencia antes de realizar la concatenación con otra secuencia:

String[] arr1 = { "abc", "bcd", "cdef", "defgh" };
String[] arr2 = { "af", "fg", "gh" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
 
Stream<String> stream3 = Stream.concat(stream1.filter(x -> x.length()<4), stream2);
String[] arr = stream3.toArray(String[]::new);
System.out.println(Arrays.toString(arr));

Producción:

[abc, bcd, af, fg, gh]

Fusión de más de 2 corrientes

String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "e", "f", "g" };
String[] arr3 = { "h", "i", "j" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
Stream<String> stream3 = Stream.of(arr3);
 
Stream<String> stream = Stream.concat(Stream.concat(stream1, stream2), stream3);
String[] arr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(arr));

Producción:

[a, b, c, d, e, f, g, h, i, j]

Tenga en cuenta que los elementos devueltos por el método Stream.concat () están ordenados. Por ejemplo, las siguientes dos líneas devuelven el mismo resultado:

Stream.concat(Stream.concat(stream1, stream2), stream3);
Stream.concat(stream1, Stream.concat(stream2, stream3));

Pero el resultado de los dos siguientes es diferente.

Stream.concat(Stream.concat(stream1, stream2), stream3); //[a, b, c, d, e, f, g, h, i, j]
Stream.concat(Stream.concat(stream2, stream1), stream3); //[e, f, g, a, b, c, d, h, i, j]

Utilice Stream.of (…). FlatMap () para fusionar

Para que el código sea más legible, también puede usar el método Stream.of () para fusionar más de dos flujos.

String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "e", "f", "g" };
String[] arr3 = { "h", "i", "j" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
Stream<String> stream3 = Stream.of(arr3);
 
//use Stream.of(T... values)
Stream<String> stream = Stream.of(stream1, stream2, stream3).flatMap(x -> x);
 
String[] arr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(arr));