5 preguntas de
entrevistas
Java SSR
Romina Acosta
1
Java Core:
¿Cuál es la diferencia
entre HashMap y
ConcurrentHashMap
y en qué casos usarías
cada uno?
Romina Acosta
1
Características
Hashmap
No es thread-safe → si varios hilos
lo modifican al mismo tiempo,
puede haber corrupción de
datos o bucles infinitos al iterar.
Permite null como clave y
valores null.
Más rápido en entornos single-
thread porque no hay bloqueos.
Operaciones (get, put, remove)
son O(1) promedio, pero no
seguras si hay concurrencia.
Romina Acosta
1
Características
ConcurrentHashMap
Thread-safe → diseñado para ser
usado en entornos
concurrentes.
Divide el mapa en segmentos
internos y sincroniza solo el
segmento necesario, evitando
bloquear todo el mapa.
No permite null ni como clave ni
como valor.
Mejor rendimiento que
sincronizar un HashMap
manualmente con
[Link]().
Romina Acosta
Si te preguntan esto,
podés decir:
“Usaría HashMap cuando el acceso
es estrictamente de un solo hilo,
porque es más rápido y no tiene
sobrecarga de sincronización. Si el
acceso es concurrente,
ConcurrentHashMap es mejor
porque ofrece seguridad de hilo sin
bloquear toda la estructura, usando
segmentación interna, lo que
mejora el rendimiento frente a un
mapa sincronizado.”
Romina Acosta
2
Colecciones y
rendimiento
Si tienes una lista muy
grande y necesitas buscar
elementos por índice
frecuentemente, ¿usarías
ArrayList o LinkedList?
¿Por qué?.
Romina Acosta
2
Array
Internamente es un array
dinámico.
Acceder a un elemento por índice
(get(index)) es O(1) porque
simplemente calcula la posición en
el array y lo retorna.
Insertar o eliminar al final es rápido
(O(1) amortizado), pero hacerlo en
el medio requiere mover
elementos (O(n)).
Ideal para:
Búsquedas frecuentes por
índice.
Recorridos secuenciales.
Colecciones donde no hay
muchas
inserciones/eliminaciones en el
medio.
Romina Acosta
2
LinkedList
Internamente es una lista
doblemente enlazada.
Buscar un elemento por índice
(get(index)) es O(n) porque debe
recorrer nodo por nodo.
Insertar/eliminar en el medio o
extremos es rápido (O(1) si ya tienes
la referencia al nodo).
Ideal para:
Muchas inserciones/eliminaciones
en posiciones aleatorias.
Cuando el tamaño de la lista
cambia constantemente.
Romina Acosta
Si te preguntan esto,
podés decir:
“Usaría ArrayList porque
internamente es un array dinámico
y acceder por índice es O(1),
mientras que LinkedList es O(n) ya
que debe recorrer nodos. En una
lista grande, la diferencia de
rendimiento sería significativa para
búsquedas por índice.”
Romina Acosta
3
Concurrencia
¿Qué es el problema de visibility
en Java multithreading y cómo
lo resuelve volatile?
Romina Acosta
3
Problema
En Java, cada hilo puede guardar
copias de variables en caché de
CPU o en registros propios.
Si un hilo modifica una
variable, esa actualización
puede no ser visible
inmediatamente para otros
hilos.
Esto ocurre porque no hay
garantía de que la variable se
“refresque” en la memoria
principal antes de que otro hilo
la lea.
Romina Acosta
3
Cómo lo resuelve
Cuando declarás una variable como
volatile:
[Link] garantizada:
Las escrituras en esa variable
se guardan inmediatamente
en la memoria principal.
Las lecturas se hacen
siempre desde la memoria
principal, no desde caché.
[Link] reordenamiento de
instrucciones que afecten esa
variable (por la Java Memory
Model).
Romina Acosta
Si te preguntan esto,
podés decir:
“El problema de visibility ocurre
cuando los cambios hechos por un
hilo en una variable no son visibles
para otros debido a la caché de CPU
o reordenamientos de
instrucciones. volatile asegura que
las escrituras se publiquen en
memoria principal y las lecturas
siempre tomen el valor más
reciente.”
Romina Acosta
4
Ciclo de vida de un
bean en Spring
Explicar el ciclo de vida de un
Bean en Spring
Romina Acosta
4
Instanciación
Spring crea el objeto del bean
usando el constructor (por
defecto o inyectado).
En este momento el bean
está vacío, sin dependencias
inyectadas.
Romina Acosta
4
Inyección de
dependencias
Spring inyecta las
propiedades necesarias (por
constructor, setter o field
injection).
Aquí el bean ya tiene todas
sus dependencias listas.
Romina Acosta
4
Callbacks de
inicialización
(opcional)
BeanNameAware → El bean recibe
su nombre dentro del contexto.
BeanFactoryAware /
ApplicationContextAware → El bean
recibe una referencia al contenedor.
@PostConstruct o
[Link]()
→ Se ejecuta lógica de inicialización
personalizada.
Bean listo para uso
Romina Acosta
4
Bean listo
Spring entrega el bean
completamente configurado a
quien lo necesite.
El bean puede ser usado durante
toda su vida útil (singleton,
prototype, etc.).
Callbacks de dest
Romina Acosta
4
Destrucción
(cuando el
contexto se cierra)
@PreDestroy o
[Link]() → Se
ejecuta lógica de limpieza (cerrar
conexiones, liberar recursos).
Esto solo aplica a beans singleton
que Spring gestiona de inicio a fin.
Romina Acosta
Si te preguntan esto,
podés decir:
“Spring primero instancia el bean, luego inyecta
sus dependencias, ejecuta métodos de
inicialización (@PostConstruct o
afterPropertiesSet()), lo deja disponible para uso
y, al cerrar el contexto, ejecuta métodos de
destrucción (@PreDestroy o destroy()).
Si el bean implementa interfaces como
BeanNameAware o ApplicationContextAware,
esas callbacks se ejecutan después de la
inyección y antes de la inicialización.”
Romina Acosta
¿Cómo
diagnosticar una
5
consulta SQL lenta
desde Java?
Conviene mostrar que tenés un
método estructurado de
diagnóstico y que podés
trabajar tanto desde el lado de
la DB como desde el código
Java.
Romina Acosta
1. Confirmar el
problema
5
Revisar logs de la aplicación
para ver cuánto tarda la
consulta (tiempos de
ejecución).
Usar herramientas como
Spring Boot Actuator,
Micrometer o logs de
Hibernate para medir:
Romina Acosta
2. Reproducir
consulta en la db
5
Ejecutarla directamente en
el motor (PostgreSQL,
Oracle, MySQL…) para
confirmar si el problema
está en la DB o en el código
Java (transformaciones,
mapeo de objetos, etc.).
Si es lenta también en la DB
→ es un problema de query
tuning.
Romina Acosta
3. Analizar el plan
de ejecución
5
Usar EXPLAIN o EXPLAIN
ANALYZE para ver:
Uso (o no) de índices.
Joins que generan nested
loops costosos.
Lecturas secuenciales (full
table scans) innecesarias.
Romina Acosta
4. Optimizar la
consulta
5
Reducir columnas (SELECT
campo1, campo2 en lugar de
SELECT *).
Eliminar joins innecesarios o
reemplazarlos por
subconsultas si es más
eficiente.
Filtrar con WHERE lo más
pronto posible.
Usar índices adecuados para
las columnas filtradas o join.
Revisar si un LEFT JOIN
puede ser un INNER JOIN
(menos costoso).
Romina Acosta
5. Revisar el
código JAVA
5
Evitar N+1 queries en
JPA/Hibernate → usar fetch
join o EntityGraph.
Verificar que no se ejecuta la
misma query en un loop.
Usar paginación
(setMaxResults,
LIMIT/OFFSET) si se trae un
dataset grande.
Revisar si el problema es la
serialización o conversión de
datos en memoria.
Romina Acosta
Si te preguntan esto,
podés decir:
“Primero mido el tiempo de la consulta
desde Java y confirmo si el problema
está en la base o en el código. Luego
ejecuto la consulta en la DB con
EXPLAIN ANALYZE para revisar índices,
joins y posibles scans completos.
Optimizo seleccionando solo los
campos necesarios, revisando joins y
agregando índices si corresponde. Del
lado de Java, verifico evitar SELECT *,
controlar el fetch size, prevenir N+1
queries y usar paginación.”
Romina Acosta
BONUS
¿Qué es N+1?
Romina Acosta
BONUS
¿Qué es N+1?
El problema de N+1 queries
ocurre cuando una consulta
inicial (la “+1”) recupera una
lista de entidades, y luego, por
cada entidad, se ejecuta otra
consulta independiente (las
“N” consultas) para traer
datos relacionados.
Esto genera muchas idas a la
base de datos, lo que puede
degradar enormemente el
rendimiento.
Romina Acosta
Ejemplo típico con
JPA/Hibernate
Supongamos que tenemos:
Y ejecutamos:
Romina Acosta
¿Qué pasa internamente?
1 consulta para traer todos los pedidos:
SELECT * FROM pedido;
N consultas adicionales (una por cada pedido) para
traer sus ítems:
SELECT * FROM item WHERE pedido_id = ?;
SELECT * FROM item WHERE pedido_id = ?;
...
Si tenés 100 pedidos, vas a hacer 1 + 100 = 101
queries.
Romina Acosta
¿Cómo evitarlo?
Fetch Join:
List<Pedido> pedidos = [Link](
"SELECT p FROM Pedido p JOIN FETCH [Link]",
[Link])
.getResultList();
EntityGraph:
EntityGraph<Pedido> graph =
[Link]([Link]);
[Link]("items");
List<Pedido> pedidos = [Link]("SELECT p
FROM Pedido p", [Link])
.setHint("[Link]", graph)
.getResultList();
Romina Acosta
Si te preguntan esto,
podés decir:
“El problema N+1 queries es cuando
se hace una query para la lista
principal y luego una query por cada
elemento para cargar relaciones.
Esto genera muchas llamadas
innecesarias a la base. Se resuelve
con JOIN FETCH, EntityGraph o
configurando el fetch
apropiadamente.”
Romina Acosta
Si te gusta el contenido,
no olvides compartirlo
Romina Acosta