Cómo crear un USB booteable de Linux Mint 20.1 paso a paso
HashMap: ejemplo paso a pasoContar cuántas veces aparece cada palabra en una cadena es un ejercicio clásico y muy útil (indexado de palabras, estadísticas, análisis de texto, etc.). En Java lo hacemos de forma muy eficiente usando las colecciones, y en especial Map/HashMap. Aquí te explico desde lo básico hasta versiones mejoradas y modernas.
Ejemplo básico (tu código explicado)
Versión mejorada (normalizar mayúsculas y eliminar puntuación)
Alternativa con Streams (Java 8+)
Leer texto desde fichero (uso práctico)
Complejidad y consideraciones
FAQ
Cuenta palabras separadas por espacios:
import java.util.HashMap;
import java.util.Map;
public class UsoDeMaps {
public static void main(String[] args) {
String frase = "hola mundo hola java mundo";
String[] palabras = frase.split(" ");
Map<String, Integer> contador = new HashMap<>();
for (String palabra : palabras) {
if (contador.containsKey(palabra)) {
contador.put(palabra, contador.get(palabra) + 1);
} else {
contador.put(palabra, 1);
}
}
for (String palabra : contador.keySet()) {
System.out.println(palabra + ": " + contador.get(palabra));
}
}
}
Qué hace:
split(" ") separa por espacios.
HashMap<String,Integer> almacena palabra → frecuencia.
Si la palabra ya existe, incrementa; si no, la pone a 1.
Al final imprime cada entrada.
Salida esperada (orden no garantizado):
hola: 2
mundo: 2
java: 1
En práctica real conviene:
Ignorar mayúsculas/minúsculas (Hola == hola).
Eliminar signos de puntuación (, . ; : ! ? ( ) etc.).
Manejar múltiples espacios y saltos de línea.
import java.util.HashMap;
import java.util.Map;
public class ContadorPalabras {
public static void main(String[] args) {
String texto = "¡Hola, Mundo! Hola... Java: mundo; java?";
Map<String, Integer> contador = contarPalabras(texto);
contador.forEach((palabra, veces) -> System.out.println(palabra + ": " + veces));
}
public static Map<String, Integer> contarPalabras(String texto) {
Map<String, Integer> contador = new HashMap<>();
// Normalizar: pasar a minúsculas
String normalizado = texto.toLowerCase();
// Eliminar puntuación (de forma sencilla)
normalizado = normalizado.replaceAll("[^\\p{L}\\p{Nd}\\s]+", " ");
// Split por cualquier cantidad de espacios en blanco
String[] palabras = normalizado.trim().split("\\s+");
for (String p : palabras) {
if (p.isEmpty()) continue;
contador.put(p, contador.getOrDefault(p, 0) + 1);
}
return contador;
}
}
Por qué funciona mejor:
toLowerCase() unifica mayúsculas/minúsculas.
[^\\p{L}\\p{Nd}\\s]+ conserva letras Unicode (\p{L}), dígitos (\p{Nd}) y espacios; elimina puntuación.
getOrDefault(key,0) simplifica el incremento.
Si quieres una solución compacta y funcional:
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
public class ContadorConStreams {
public static void main(String[] args) {
String texto = "Hola mundo hola JAVA, mundo!";
Map<String, Long> frecuencias = Arrays.stream(
texto.toLowerCase()
.replaceAll("[^\\p{L}\\p{Nd}\\s]+", " ")
.trim()
.split("\\s+")
)
.filter(s -> !s.isEmpty())
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));
frecuencias.forEach((k,v) -> System.out.println(k + ": " + v));
}
}
Resultado: Map<String,Long> con conteos. Ventajas: expresivo, usa Collectors.groupingBy.
Si tienes un archivo grande, procesa línea a línea para ahorrar memoria:
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
public class ContadorDesdeFichero {
public static Map<String, Integer> contarDesdeFichero(Path ruta) throws IOException {
Map<String, Integer> contador = new HashMap<>();
try (BufferedReader br = Files.newBufferedReader(ruta)) {
String linea;
while ((linea = br.readLine()) != null) {
String normal = linea.toLowerCase().replaceAll("[^\\p{L}\\p{Nd}\\s]+", " ");
String[] palabras = normal.trim().split("\\s+");
for (String p : palabras) {
if (p.isEmpty()) continue;
contador.put(p, contador.getOrDefault(p, 0) + 1);
}
}
}
return contador;
}
}
Complejidad temporal: O(n) donde n es el número de tokens (palabras). Cada inserción/actualización en HashMap es O(1) promedio.
Complejidad espacial: O(m) donde m es el número de palabras distintas (vocabulario).
Unicode y locales: toLowerCase() sin argumentos usa el Locale por defecto; para texto en turco u otros idiomas con reglas especiales podrías usar toLowerCase(Locale.ROOT) o el locale apropiado.
Tokenización avanzada: si necesitas tokenizar mejor (contracciones, apóstrofes, idiomas complejos), considera usar una librería de NLP (por ejemplo OpenNLP, ICU4J, etc.).
Mayúsculas/acentos: la normalización que quita puntuación no modifica tildes; si quieres normalizar acentos (á -> a) usa java.text.Normalizer y elimina marcas diacríticas.
Ejemplo para quitar tildes:
import java.text.Normalizer;
String sinAcentos = Normalizer.normalize(texto, Normalizer.Form.NFD)
.replaceAll("\\p{M}", ""); // elimina marcas diacríticas¿Por qué usar HashMap y no otro Map?
HashMap es rápido (O(1) promedio) y adecuado para conteos. Si necesitas orden de inserción, usa LinkedHashMap; si quieres orden por clave, TreeMap (O(log n)).
¿Qué ocurre con la puntuación y símbolos?
En la versión mejorada reemplazamos cualquier caracter que no sea letra o dígito por espacio. Ajusta la expresión regular según tus necesidades (por ejemplo, conservar apostrofes).
¿Cómo hago que la comparación sea insensible a mayúsculas?
Convierte todo a minúsculas con toLowerCase() antes de contar.
¿Cómo ordeno los resultados por frecuencia?
Puedes transformar el Map en una lista de entradas y ordenarla:
contador.entrySet().stream() .sorted(Map.Entry.<String,Integer>comparingByValue().reversed()) .forEach(System.out::println);
¿Y si el texto es muy grande?
Procesa por bloques o por líneas (como en el ejemplo de fichero) para no cargar todo en memoria; también puedes usar Map en disco o bases de datos si el vocabulario es enorme.
¿Puedo usar este método para análisis multilingüe?
Sí, pero tienes que adaptar la normalización y tokenización a cada idioma y, posiblemente, usar librerías NLP que gestionen mejor tokenización y stemming/lemmatization.
¿Por qué usar HashMap y no otro Map?
HashMap es rápido (O(1) promedio) y adecuado para conteos. Si necesitas orden de inserción, usa LinkedHashMap; si quieres orden por clave, TreeMap (O(log n)).
¿Qué ocurre con la puntuación y símbolos?
En la versión mejorada reemplazamos cualquier caracter que no sea letra o dígito por espacio. Ajusta la expresión regular según tus necesidades (por ejemplo, conservar apostrofes).
¿Cómo hago que la comparación sea insensible a mayúsculas?
Convierte todo a minúsculas con toLowerCase() antes de contar.
¿Cómo ordeno los resultados por frecuencia?
Puedes transformar el Map en una lista de entradas y ordenarla:
contador.entrySet().stream() .sorted(Map.Entry.<String,Integer>comparingByValue().reversed()) .forEach(System.out::println);
¿Y si el texto es muy grande?
Procesa por bloques o por líneas (como en el ejemplo de fichero) para no cargar todo en memoria; también puedes usar Map en disco o bases de datos si el vocabulario es enorme.
¿Puedo usar este método para análisis multilingüe?
Sí, pero tienes que adaptar la normalización y tokenización a cada idioma y, posiblemente, usar librerías NLP que gestionen mejor tokenización y stemming/lemmatization.
Comentarios
Publicar un comentario