{
 "subtemas": [],
 "tarjetas": [
  {
   "sub": "String y texto",
   "q": "¿Qué es el String pool y cómo funciona?",
   "a": "Área del heap donde la JVM guarda Strings únicos. Los literales (\"hola\") se internan automáticamente: dos literales iguales son el MISMO objeto.\nnew String(\"hola\") crea objeto FUERA del pool (por eso == da false); .intern() lo mete/busca en el pool.\nAhorra memoria y permite comparación rápida.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "Métodos esenciales de String que hay que saberse",
   "a": "length, charAt, substring(begin[,end]) — end exclusivo, indexOf/lastIndexOf, contains, startsWith/endsWith, replace, split, trim/strip (strip es Unicode, Java 11), toUpper/LowerCase, isEmpty/isBlank (sin contenido o solo espacios), equals/equalsIgnoreCase, compareTo, chars(), repeat, join, format.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "¿Por qué concatenar String en un loop es un problema?",
   "a": "Cada + crea un String nuevo y copia todo el contenido → O(n²) en el loop.\nSolución: StringBuilder en un hilo (append), StringBuffer solo si varios hilos comparten el builder (raro, está sincronizado).\nEl compilador optimiza concatenaciones EN UNA MISMA expresión, no en loops.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "StringBuilder vs StringBuffer vs String.concat",
   "a": "StringBuilder: mutable, NO sincronizado (el normal desde Java 5).\nStringBuffer: igual pero sincronizado (legado).\nconcat/+ para pocas uniones.\nCapacidad inicial 16; crece duplicando+2. new StringBuilder(tamañoEstimado) evita reallocs.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "split() y su trampa con espacios finales y caracteres especiales",
   "a": "split usa regex: split(\".\") NO funciona (punto = 'cualquier carácter') → escapar split(\"\\\\.\").\nLos strings vacíos del FINAL se descartan por defecto: \"a,b,,\".split(\",\") → [a,b].\nUsar split(\",\", -1) para conservarlos (útil en CSV).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "String.format y text blocks",
   "a": "format(\"%s tiene %d años\", nombre, edad): %s string, %d entero, %f decimal (%.2f 2 decimales), %n salto, %% literal.\nText blocks (Java 15+): \"\"\" ... \"\"\" multilinea; quita indentación común (incidental), respeta saltos; útiles para JSON/SQL legible.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "trim() vs strip() vs isBlank()",
   "a": "trim() elimina caracteres <= espacio (ASCII, legado).\nstrip() (Java 11) usa Character.isWhitespace — Unicode correcto.\nisBlank() true si vacío o solo whitespace.\nisEmpty() solo true con length 0.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "Regex en Java: Pattern y Matcher",
   "a": "Pattern p = Pattern.compile(\"regex\"); Matcher m = p.matcher(texto); m.find() itera coincidencias, m.group() la extrae, m.matches() exige coincidencia total.\nGrupos con paréntesis: m.group(1).\nBackslashes dobles en Java: \"\\\\d+\".\nPara validar simple: cadena.matches(\"regex\").\nCompilar el Pattern UNA vez si se reutiliza (thread-safe).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "Comparar Strings correctamente en todos los casos",
   "a": "SIEMPRE a.equals(b), no ==.\nIgnorando mayúsculas: equalsIgnoreCase o mejor compareTo case-normalized.\nNull-safe: Objects.equals(a,b) o \"constante\".equals(variable).\nOrden natural: compareTo (0 = iguales) — cuidado con null (NPE).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "¿Cuánta memoria ocupa un String y por qué importa?",
   "a": "Objeto String ≈ 24B cabecera + array de bytes. Desde Java 9 es byte[] con codificación compacta (Latin-1 si basta, UTF-16 si no) — menos memoria que el char[] antiguo.\nDuplicados en colecciones grandes → intern() o mapas de deduplicación.\nSubstrings ya NO comparten array (copia) desde Java 7u6.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "String.join y StringJoiner",
   "a": "String.join(\", \", \"a\", \"b\") o String.join(delim, lista).\nStringJoiner permite prefijo/sufijo: new StringJoiner(\", \", \"[\", \"]\").\nEquivalente en streams: Collectors.joining(\", \", \"[\", \"]\").",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "¿Cómo iterar los caracteres de un String moderno?",
   "a": "chars() devuelve IntStream: \"abc\".chars().filter(c -> c != 'a')...\nLoop clásico con charAt(i) para índices.\nOJO: codePoints() para Unicode real (emojis ocupan 2 chars; chars() parte los surrogate pairs).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "Conversión String ↔ número y sus excepciones",
   "a": "Integer.parseInt(\"42\") → int; valueOf(\"42\") → Integer. Double.parseDouble, Long.parseLong.\nFormato inválido → NumberFormatException (checked? NO, unchecked).\nConvertir número a String: String.valueOf(x) o Integer.toString(x).\nBase distinta: parseInt(\"ff\", 16) → 255.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "String y texto",
   "q": "¿Qué es un text block y cómo maneja la indentación?",
   "a": "\"\"\"...\"\"\" (Java 15+). El compilador calcula la indentación MÍNIMA común (incluida la línea de cierre) y la quita.\nSecuencias: \\s preserva espacio al final de línea, \\ evita el salto de línea (concatenar), \\n explícito si se quiere.\nNo interpola variables: usar .formatted() o format.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Jerarquía del Collection Framework (mapa mental)",
   "a": "Iterable → Collection → {List, Set, Queue}. Map aparte (no es Collection).\nList: ArrayList, LinkedList, Vector(legacy), CopyOnWriteArrayList.\nSet: HashSet, LinkedHashSet, TreeSet, EnumSet.\nQueue/Deque: ArrayDeque, PriorityQueue, LinkedList.\nMap: HashMap, LinkedHashMap, TreeMap, ConcurrentHashMap, Hashtable(legacy), EnumMap.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "HashSet: cómo decide si un elemento 'ya está'",
   "a": "1) calcula hashCode() del objeto para elegir bucket, 2) dentro del bucket compara con equals().\nSi sobrescribes equals sin hashCode (o el objeto MUTA su hash después de insertar) → contains() falla.\nPor eso: objetos clave inmutables o no mutar campos usados en hash tras insertar.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "TreeSet/TreeMap: qué garantizan y qué cuestan",
   "a": "Mantienen elementos ORDENADOS según Comparable/Comparator (árbol rojo-negro).\nCosto: add/remove/contains O(log n) vs O(1) de HashSet/HashMap.\nExtras navegables: first/last, floor/ceiling, headSet/tailSet, descendingSet.\nNo admiten null (con orden natural) y rompen el contrato si el comparador es inconsistente con equals.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "LinkedHashMap y LinkedHashSet: el punto medio perfecto",
   "a": "Hash O(1) + ORDEN DE INSERCIÓN predecible (lista doble enlazada interna).\nAccess-order mode: new LinkedHashMap(16, 0.75f, true) reordena por acceso → base perfecta para una caché LRU (sobrescribir removeEldestEntry).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "ArrayList: cómo crece internamente",
   "a": "Array interno; al llenarse crece ~50% (nueva capacidad = vieja*1.5) y copia elementos.\nensureCapacity()/constructor con capacidad evita reallocs cuando sabes el tamaño.\nremove/contains O(n); trimToSize() libera sobrante.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "ArrayDeque vs Stack vs LinkedList para pilas y colas",
   "a": "ArrayDeque: la opción recomendada — array circular, O(1) amortizado en ambos extremos, menos GC.\njava.util.Stack hereda de Vector (sincronizado, legado, diseño malo) → evitar.\nLinkedList también implementa Deque pero con overhead de nodos y peor localidad de caché.\nMétodos: push/pop/peek, offer/poll/element, addFirst/last.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "PriorityQueue: comportamiento y trampas",
   "a": "Heap binario: el elemento de MAYOR prioridad sale primero (el menor con orden natural).\npoll() O(log n), peek O(1); NO está ordenada internamente (iterar no da orden).\nNo admite null; con objetos necesita Comparable/Comparator.\nUso: top-K, schedulers, Dijkstra.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "fail-fast vs fail-safe en iteradores",
   "a": "Fail-fast (ArrayList, HashMap): detectan modificación estructural durante la iteración (contador modCount) → ConcurrentModificationException. No es para hilos, es detección de bugs.\nFail-safe: CopyOnWriteArrayList (itera sobre snapshot) y ConcurrentHashMap (iterador débilmente consistente) no lanzan.\nEliminar mientras iteras: usar iterator.remove(), no lista.remove().",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "¿Cómo eliminar elementos de una lista mientras la recorres?",
   "a": "1) Iterator: it.remove() — el clásico correcto.\n2) removeIf(predicado) — Java 8+, el más limpio (interno usa iterator).\n3) Recolectar en nueva lista (si no quieres mutar).\nMAL: for-each con lista.remove(x) → ConcurrentModificationException.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Comparable vs Comparator aplicado a ordenar colecciones",
   "a": "lista.sort(comparator) o Collections.sort(lista).\nComparator.comparing(User::getApellido).thenComparing(User::getEdad).reversed().\nnullsFirst/nullsLast para nulls.\nComparar descendente: comparing(X).reversed() o .reversed() al final de la cadena completa (ojo: reversed() revierte TODO lo acumulado).",
   "nivel": "basico",
   "clave": true
  },
  {
   "sub": "Colecciones",
   "q": "List.of() vs Arrays.asList() vs new ArrayList<>()",
   "a": "List.of() (Java 9+): inmutable de verdad — add/remove/set lanzan UnsupportedOperationException; no permite nulls.\nArrays.asList(): vista de tamaño FIJO sobre el array — set() sí funciona, add/remove no; refleja cambios del array.\nnew ArrayList<>(): mutable completa.\nPasar una de las fijas a código que muta → excepción en runtime.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Colecciones no modificables vs inmutables",
   "a": "Collections.unmodifiableList(vista): solo prohibe modificar LA VISTA; si la lista de fondo cambia, la vista la refleja.\nList.of()/Set.of()/Map.of() (Java 9+): inmutables de verdad, copian los datos.\nPara exponer estado interno: copiar o devolver inmutable.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Métodos de Map modernos que quitan muchos ifs",
   "a": "getOrDefault, putIfAbsent, computeIfAbsent (patrón memoización: map.computeIfAbsent(k, x -> new ArrayList<>()).add(v)), computeIfPresent, merge (contador: map.merge(k, 1, Integer::sum)), forEach(k,v), getOrDefault.\nput devuelve el valor ANTERIOR (null si no había).",
   "nivel": "basico",
   "clave": true
  },
  {
   "sub": "Colecciones",
   "q": "Complejidades que hay que memorizar",
   "a": "ArrayList: get O(1), add final amortizado O(1), add/remove medio O(n), contains O(n).\nHashMap/HashSet: O(1) medio, O(log n) peor (treeify).\nTreeMap/TreeSet: O(log n).\nLinkedList: add extremos O(1), get i O(n).\nArrayDeque: extremos O(1).\nPriorityQueue: offer/poll O(log n).\nBinary search: O(log n) (requiere orden).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "ConcurrentHashMap: qué lo hace diferente de un HashMap sincronizado",
   "a": "Segmentación/CAS: permite lecturas concurrentes sin lock y escrituras con locks finos por nodo — mucho mejor throughput.\nNO permite claves/valores null.\nOperaciones compuestas atómicas: putIfAbsent, compute, merge.\nIterador débilmente consistente (no lanza CME).\nCollections.synchronizedMap bloquea TODO el mapa → cuello de botella.",
   "nivel": "intermedio",
   "clave": true
  },
  {
   "sub": "Colecciones",
   "q": "¿Cuándo CopyOnWriteArrayList?",
   "a": "Cuando hay MUCHÍSIMAS lecturas y casi cero escrituras (listeners, configs).\nCada escritura copia el array completo → costosa, pero los iteradores nunca ven CME ni cambios (snapshot).\nMal para listas que mutan seguido.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Colecciones y null: quién lo permite y quién no",
   "a": "Permiten null: ArrayList, LinkedList, HashMap (una clave null), HashSet, LinkedHashMap.\nNO permiten: TreeMap/TreeSet (con orden natural, lanza NPE al comparar), ConcurrentHashMap, Hashtable, Queue concurrentes, List.of/Set.of/Map.of, PriorityQueue.\nDiseño moderno: evitar nulls (Optional, valores default).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "EnumSet y EnumMap: por qué los menciona todo entrevistador",
   "a": "Implementaciones especiales para enums basadas en arrays de bits/array plano: muchísimo más rápidas y compactas que HashSet/HashMap de enums.\nEnumSet.of(Dia.LUN, Dia.VIE), EnumMap?key, Valor>.\nOrden = orden de declaración del enum.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Collections utility: métodos que salvan vidas",
   "a": "sort, shuffle, reverse, swap, binarySearch (requiere orden), min/max con comparator, frequency, disjoint, unmodifiableX, synchronizedX, emptyList/emptyMap, singletonList, nCopies, addAll, rotate.\nMuchos tienen equivalente moderno en la propia interfaz (list.sort, list.forEach).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Capacidad inicial y factor de carga: ¿por qué afinarlos?",
   "a": "HashMap default: capacidad 16, factor 0.75 → resize a las 12 inserciones (duplica y rehash).\nSi sabes que guardarás 1M entradas: new HashMap<>(1_500_000) evita ~17 resizes.\nCapacidad deseada = esperado / 0.75 (+1 para hash seguro).\nArrayList: igual idea con constructor.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "¿Cómo funciona el resize de HashMap y su costo?",
   "a": "Al superar capacidad*loadFactor: crea array del doble y RE-DISTRIBUYE todas las entradas (rehash). Costo O(n) puntual, amortizado O(1) por inserción.\nJava 8 conserva el orden relativo dentro de buckets (split por bit alto), antes invertía listas.\nCon muchas inserciones previstas, presizar el mapa evita trabajo.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "Iterator: contrato y uso correcto",
   "a": "hasNext(), next(), remove() (único borrado seguro durante iteración).\nSolo se avanza hacia adelante, una pasada.\nListIterator además: hasPrevious/previous, add, set, índices — bidireccional en List.\nLanzan ConcurrentModificationException si la colección cambia por fuera.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "¿Por qué Map no extiende de Collection?",
   "a": "Porque es un mapeo clave→valor, no una colección de elementos: sus 'colecciones' derivadas son keySet(), values() y entrySet() (esas SÍ son Collection/View).\nDiseño: Map es otra abstracción distinta; se iteran sus entradas con entrySet().",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "entrySet vs keySet para recorrer un Map",
   "a": "Si necesitas clave Y valor: entrySet() — una sola búsqueda.\nkeySet() + get(k) hace DOBLE hash por entrada (innecesario).\nforEach((k,v) -> ...) es el moderno.\nvalues() cuando solo importan los valores.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "¿Qué son las vistas (views) de colecciones?",
   "a": "Objetos que ENVUELVEN otro sin copiar: keySet(), subList(), unmodifiableList(), Arrays.asList().\nCambios en el respaldo se reflejan en la vista (y a veces al revés: subList escribe en la original).\nPeligro: mutar el respaldo invalida la vista (subList → ConcurrentModificationException).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "¿List.contains con objetos propios devuelve false? Los 3 sospechosos",
   "a": "1) equals() no sobrescrito (compara referencias).\n2) equals sobrescrito pero hashCode no (en HashSet/HashMap).\n3) El objeto MUTÓ un campo que participa en equals/hashCode después de insertarlo.\nFix: equals+hashCode consistentes + objetos clave inmutables.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Colecciones",
   "q": "remove(int index) vs remove(Object) en List: la trampa clásica",
   "a": "List<Integer> nums: remove(1) elimina el ÍNDICE 1; remove(Integer.valueOf(1)) elimina el OBJETO 1.\nEl compilador elige por el tipo estático del argumento.\nFuente infinita de bugs con listas de números.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "¿Para qué existen los genéricos y qué problema arreglan?",
   "a": "Tipado seguro en compilación para colecciones/algoritmos reutilizables: List<String> garantiza que solo hay Strings (antes: raw types y casts a mansalva con ClassCastException en runtime).\nEliminan casts explícitos y documentan el contrato del tipo.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "¿Qué es el type erasure y sus consecuencias?",
   "a": "El compilador BORRA los parámetros de tipo: List<String> y List<Integer> son la misma clase en runtime (List).\nConsecuencias: NO hay new T(), T.class, ni instanceof List<String>, ni arrays genéricos new T[]; los genéricos no participan en sobrecarga (f(String) vs f(Integer) ok, pero f(List<String>) vs f(List<Integer>) NO).\nLos tipos acotados se reemplazan por su bound y se insertan casts automáticos.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "Wildcard ? extends vs ? super (regla PECS)",
   "a": "PECS: Producer Extends, Consumer Super.\n? extends T: productor — LEES elementos como T, NO puedes agregar (solo null): List<? extends Number> acepta List<Integer>.\n? super T: consumidor — ESCRIBES T, lo que lees es Object.\nSi solo lees: extends; si solo escribes: super; si ambos: tipo exacto.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "Bounded types: <T extends Comparable<T>>",
   "a": "Limita T a un tipo o subtipos: <T extends Number> permite usar métodos de Number sobre T.\nMúltiples bounds: <T extends Comparable<T> & Serializable> (clase primero, interfaces después).\nEn métodos: static <T extends Comparable<T>> T max(List<T> lista).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "¿Por qué no hay List<int>?",
   "a": "Los genéricos solo aceptan tipos de referencia (type erasure + Object interno).\nList<int> no existe → List<Integer> con autoboxing (overhead de memoria/GC).\nAlternativas de rendimiento: IntStream, librerías (fastutil, Eclipse Collections) o arrays primitivos.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "Métodos genéricos: sintaxis y cuándo",
   "a": "static <T> T primero(List<T> lista) — el <T> va ANTES del tipo de retorno.\nSe infiere del contexto: String s = primero(nombres).\nÚtil cuando el método es genérico aunque la clase no, o para vincular tipos de argumento y retorno entre sí.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "Raw types y por qué el compilador los marca",
   "a": "List sin parámetro = raw type: vuelve al mundo pre-Java 5 (acepta cualquier cosa, casts inseguros).\nNO es lo mismo que List<Object> (esta es tipada y segura).\nUsar raw types genera unchecked warnings y bugs silenciosos; solo aparecen por interoperabilidad legada.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "Herencia con genéricos: ¿List<Dog> es subtipo de List<Animal>?",
   "a": "NO (invarianza) — aunque Dog extends Animal. Si lo fuera, podrías meter un Cat en una List<Dog> vía la referencia List<Animal> y romper todo.\nLo correcto: List<? extends Animal> para leer.\nG = G<D> solo si el tipo parametrizado coincide EXACTO: ArrayList<Dog> sí es Collection<Dog>.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "Genéricos, static y arrays: las tres prohibiciones",
   "a": "1) Campos static NO pueden usar T de la clase (T existe por instancia).\n2) No se pueden crear arrays genéricos: new T[10], new List<String>[10] (erasure no puede chequear; workaround: (T[]) new Object[10] con @SuppressWarnings).\n3) No instanceof con tipos parametrizados (solo List<?> o clase cruda).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "¿Qué es el wildcard capture y el helper idiom?",
   "a": "El compilador trata cada uso de ? como un tipo desconocido ÚNICO que no puede nombrarse: no puedes lista.set(0, lista.get(0)) con List<?>.\nTruco: helper genérico privado <T> void swapHelper(List<T> l){...} y llamarlo desde el método con wildcard — el compilador captura el wildcard como T real.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Genéricos",
   "q": "@SuppressWarnings(\"unchecked\"): cuándo es legítimo",
   "a": "Solo cuando TÚ garantizas la seguridad de tipo que el compilador no puede probar (casts de erasure, APIs legadas).\nAnotar la variable/método MÍNIMO posible, con comentario de por qué es seguro.\nSi se suprime a nivel de clase, se esconden bugs reales.",
   "nivel": "intermedio",
   "clave": false
  }
 ]
}