CodeGym /Blog Java /Random-ES /Explorando preguntas y respuestas de una entrevista de tr...
John Squirrels
Nivel 41
San Francisco

Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java. parte 10

Publicado en el grupo Random-ES
¡Hola! ¿Cuántas horas se necesitan para convertirse en un maestro en algo? A menudo he oído algo como: "Para convertirse en un maestro en algo, es necesario dedicarle 10.000 horas". Es una cifra intimidante, ¿no? Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java.  Parte 10 - 1Aún así, me pregunto si es verdad. Y constantemente trato de calcular cuántas horas he invertido ya en dominar el arte de la programación. Y cuando cruce esa línea especial de 10.000 horas y me convierta en un maestro, ¿sentiré la diferencia? ¿O ya crucé esa línea hace mucho tiempo sin darme cuenta? De cualquier manera, no es necesario invertir tanto tiempo para convertirse en programador. Lo importante es utilizar tu tiempo sabiamente. Su objetivo principal es conseguir una entrevista. Y en las entrevistas, a los aspirantes a desarrolladores de software primero se les pregunta sobre la teoría, por lo que debe ser una fortaleza. De hecho, mientras se prepara para una entrevista, su trabajo es descubrir todas las lagunas en el conocimiento de la teoría básica de Java y luego llenarlas. Hoy estoy aquí para ayudarte a hacer precisamente eso, ya que hoy continuaremos nuestra revisión de las preguntas de entrevistas más populares. Bueno, ¡continuemos!

89. ¿En qué se diferencia una ArrayList de una LinkedList?

Esta es una de las preguntas más populares, junto con la pregunta sobre la estructura interna de un HashMap . Ninguna entrevista está completa sin él, por lo que su respuesta debería salir fácilmente de su lengua. Además de lo obvio (tienen nombres diferentes), se diferencian en su estructura interna. Anteriormente, analizamos la estructura interna de ArrayList y LinkedList , por lo que no profundizaré en los detalles de su implementación. Sólo te recordaré que ArrayList se implementa utilizando una matriz interna cuyo tamaño aumenta dinámicamente según esta fórmula:
<size of the current array> * 3 / 2 + 1
Además, la implementación de una LinkedList utiliza una lista interna doblemente enlazada, es decir, cada elemento tiene una referencia al elemento anterior y siguiente, excepto los elementos al inicio y al final de la lista. A los entrevistadores les encanta hacer esta pregunta: "¿Cuál es mejor, ArrayList o LinkedList ?" esperando atraparte. Al fin y al cabo, si dices que uno u otro es mejor, entonces has dado la respuesta equivocada. Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java.  Parte 10 - 2En cambio, debes aclarar la situación específica de la que estás hablando: acceder a elementos por índice o insertarlos en el medio de la lista. Luego, dependiendo de su respuesta, podrás explicar cuál es mejor. Anteriormente describí cómo funcionan ArrayList y LinkedList en cada situación. Resumamos esto poniéndolos en una fila para comparar: Agregar un elemento (add)
  1. Si no se especifica un índice, se agregará automáticamente un nuevo elemento al final para ambos tipos de listas. En LinkedList , el nuevo elemento se convertirá en la nueva cola (solo se reescribirán un par de referencias, por lo que la complejidad algorítmica es O(1) ).

    El método add agrega un elemento a la última celda vacía de la matriz ( O(1) ).

  2. Agregar un elemento por índice generalmente significa insertarlo en algún lugar en el medio de la lista. En una LinkedList , el método primero buscará la ubicación deseada iterando sobre los elementos de la cola y la cabeza ( O(n/2) ) y luego insertará el valor sobrescribiendo las referencias de los elementos en ambos lados de donde está Se inserta un nuevo elemento ( O(1) ). La complejidad algorítmica general de esta operación será O(n/2) .

    En la misma situación (agregando por índice), un ArrayList encuentra la ubicación deseada ( O(1) ) y luego desplaza todos los elementos ubicados a la derecha (incluido el elemento ya almacenado en el índice especificado) hacia la derecha en uno (que puede requerir la creación de una nueva matriz interna y copiar elementos en ella) ( O(n/2) ). La complejidad general es O(n/2) .

  3. Agregar un elemento al principio de una LinkedList es similar a agregar un elemento al final: el nuevo elemento se convierte en el nuevo encabezado ( O(1) ). Pero para una ArrayList, esa operación requiere mover todos los elementos hacia la derecha ( O(n) ).

La conclusión es que para una LinkedList la complejidad algorítmica variará de O(1) a O(n/2) . Otra observación es que cuanto más cerca esté la inserción del final o del principio de la lista, más rápida será. Para ArrayList , la complejidad algorítmica varía de O(1) a O(n) y cuanto más cerca esté la inserción del final de la lista, más rápida será. Establecer un elemento (conjunto) Esta operación escribe un elemento en la posición especificada en la lista, sobrescribiendo cualquier elemento existente. En LinkedList , esta operación es similar a sumar, ya que el mayor desafío aquí es encontrar la ubicación del elemento. El elemento existente se sobrescribe actualizando un par de referencias, por lo que nuevamente tenemos una complejidad algorítmica que varía de O(1) a O(n/2) , dependiendo de la distancia de la posición deseada desde el final o el principio de la lista. Pero para ArrayList , esta operación encuentra la celda deseada por índice y escribe el nuevo elemento allí. Al igual que la operación de conjunto, la búsqueda por índice tiene una complejidad algorítmica de O(1) . Obtener un elemento por índice (get) Obtener un elemento de una LinkedList sigue el mismo principio de búsqueda que se utiliza en otras operaciones. La complejidad depende de la distancia desde el final o el principio, es decir, varía de O(1) a O(n/2) . Como se señaló anteriormente, para ArrayList , encontrar un elemento por índice en la matriz interna tiene una complejidad de O(1) . Eliminar un elemento por índice (eliminar) Para LinkedList , se aplica el mismo principio una vez más. Primero se localiza el elemento, y luego se reescriben las referencias, los vecinos del elemento eliminado ahora se refieren entre sí, eliminando las referencias al elemento eliminado, que posteriormente será limpiado por el recolector de basura. En otras palabras, la complejidad algorítmica sigue siendo la misma: varía de O(1) a O(n/2) . Para ArrayList , esta operación es más como agregar un nuevo elemento (agregar). Primero, el método encuentra el elemento deseado ( O(1) ), lo elimina y luego todos los elementos ubicados a la derecha se desplazan un paso hacia la izquierda para cerrar la brecha creada por la eliminación. Eliminar un elemento tiene la misma complejidad algorítmica que la operación de agregar: de O(1) a O(n). Cuanto más cerca esté el elemento eliminado del final de la lista, menor será la complejidad algorítmica de esta operación. Y ahora hemos cubierto todas las operaciones principales. Permítame recordarle que al comparar estos dos tipos de listas, es necesario aclarar la situación específica en la que se utilizan. Sólo entonces podrá responder inequívocamente a la pregunta del entrevistador.

90. ¿En qué se diferencia un ArrayList de un HashSet?

Si pudiéramos comparar ArrayList y LinkedList operación por operación para determinar cuál es mejor, no nos resultaría tan fácil hacer esa comparación entre ArrayList y HashSet , porque son colecciones completamente diferentes. Puedes comparar un postre con otro, pero comparar un postre y un plato salado es un desafío: son dolorosamente diferentes. Aún así, intentaré señalar algunas de las diferencias entre ellos:
  • ArrayList implementa la interfaz List mientras que HashSet implementa la interfaz Set .

  • ArrayList le permite acceder a un elemento por índice: la operación de obtención tiene una complejidad algorítmica O(1) , pero HashSet solo le permite acceder a un elemento deseado mediante iteración, lo que produce una complejidad algorítmica que va de O(1) a O(n) .

  • ArrayList permite elementos duplicados. En un HashSet , todos los elementos son únicos: cualquier intento de agregar un elemento que ya esté presente en un HashSet fallará (los duplicados se verifican mediante código hash, de ahí el nombre de esta colección).

  • ArrayList se implementa usando una matriz interna, pero HashSet se implementa usando un HashMap interno .

  • ArrayList mantiene el orden de inserción de los elementos, pero HashSet es un conjunto desordenado y no mantiene el orden de los elementos.

  • ArrayList permite cualquier número de valores nulos, pero solo puede agregar un valor nulo a un HashSet (después de todo, los elementos deben ser únicos).

91. ¿Por qué Java tiene tantas implementaciones de matrices dinámicas diferentes?

Ésta es más una cuestión filosófica. También podríamos preguntarnos ¿por qué se les ocurren tantas tecnologías nuevas y variadas? Por conveniencia. Y lo mismo ocurre con una gran cantidad de implementaciones de matrices dinámicas. Ninguno de ellos puede considerarse la implementación mejor o ideal. Cada uno tiene sus ventajas en situaciones específicas. Nuestro trabajo es conocer sus diferencias y sus fortalezas/debilidades para poder utilizar la colección que mejor se adapte a cada situación.

92. ¿Por qué Java tiene tantas implementaciones diferentes de almacenamiento de valores-clave?

Aquí la situación es la misma que con las implementaciones de matrices dinámicas. Definitivamente no hay ninguno que sea universalmente mejor que los demás: cada uno tiene fortalezas y debilidades. Y hay que aprovechar sus puntos fuertes, por supuesto. Ejemplo: el paquete concurrente, que tiene muchas clases multiproceso, tiene sus propias colecciones concurrentes . La clase ConcurrentHashMap tiene una ventaja sobre el HashMap estándar en términos de seguridad cuando se trabaja con datos en un entorno de subprocesos múltiples, pero eso tiene el costo de un rendimiento más lento. Y las implementaciones que no son la mejor opción en ninguna situación dejan de utilizarse gradualmente. Por ejemplo: Hashtable , que originalmente estaba destinado a ser un HashMap seguro para subprocesos , se ha olvidado y ha dejado de usarse, porque ConcurrentHashMap es incluso mejor que Hashtable cuando se trabaja en un entorno de subprocesos múltiples.

93. ¿Cómo clasifico una colección de elementos?

Lo primero que hay que decir es que la clase que representa los elementos de la colección debe implementar la interfaz Comparable , que consta del método compareTo . O necesita una clase que implemente la interfaz Comparator , incluido su método de comparación . Ambos métodos indican cómo comparar objetos de un tipo determinado. Esto es fundamental a la hora de ordenar, porque el algoritmo de clasificación necesita comprender qué principio utilizar para comparar elementos. Esto se hace principalmente implementando Comparable directamente en la clase que desea ordenar. Usar Comparator es menos común. Supongamos que estás usando una clase de alguna biblioteca y no implementa Comparable , pero necesitas ordenar una colección de sus objetos. Como no puedes cambiar el código de esta clase (excepto extendiéndolo), puedes escribir una implementación de Comparator que indique cómo comparar objetos de la clase. Y un ejemplo más. Si necesita ordenar objetos del mismo tipo de diferentes maneras, puede escribir múltiples implementaciones de Comparator para usarlas en diferentes situaciones. Como regla general, muchas clases listas para usar, por ejemplo String , ya implementan la interfaz Comparable . Eso significa que no necesita preocuparse por cómo comparar estas clases. Puedes seguir adelante y usarlos. La primera y más obvia forma es utilizar la clase TreeSet o TreeMap . Estas clases almacenan elementos en orden según el comparador implementado por los elementos de la clase. No olvide que TreeMap ordena claves, no valores. Si usa Comparator en lugar de Comparable , entonces deberá pasar un objeto Comparator al constructor de la colección cuando la cree:
TreeSet treeSet = new TreeSet(customComparator);
¿Pero qué pasa si tienes un tipo diferente de colección? ¿Cómo lo ordenas? En este caso, la segunda forma de la clase de utilidad Colecciones , el método sort() , es adecuada. El método es estático, por lo que todo lo que necesita es anteponer el nombre de la clase y luego pasar la lista para ordenar. Por ejemplo:
Collections.sort(someList);
Si está utilizando una implementación de Comparator en lugar de Comparable , debe pasarla como segundo argumento:
Collections.sort(someList, customComparator);
Esta operación cambiará el orden interno de los elementos en la lista pasada: la lista se ordenará usando el comparador. Tenga en cuenta que la lista pasada debe ser mutable; de ​​lo contrario, el método fallará y generará una excepción UnsupportedOperationException . Una tercera opción es utilizar el método ordenado de la clase Stream , que ordena los elementos de la colección. Si usamos Comparable :
someList = someList.stream().sorted().collect(Collectors.toList());
Si usamos Comparador :
someList = someList.stream().sorted(customComparator).collect(Collectors.toList());
La cuarta forma es implementar manualmente un algoritmo de clasificación, por ejemplo, clasificación por burbujas o clasificación por fusión .

Clase de objeto. es igual () y hashCode ()

94. Dé una breve descripción de la clase Objeto en Java.

En la segunda parte de la revisión, ya analizamos los métodos de la clase Objeto . Aquí les recordaré que la clase Objeto es un antepasado de todas las clases en Java. Tiene 11 métodos, que a su vez son heredados por todas las clases. Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java.  Parte 10 - 3

95. ¿Para qué se utilizan iguales () y hashCode () en Java?

hashCode() es un método de la clase Object que heredan todas las clases. Su trabajo es generar un número que represente un objeto específico. Se puede encontrar un ejemplo de este método en acción en HashMap , donde se llama a objetos clave para obtener el código hash local, que determinará en qué depósito (celda de la matriz interna) se almacenará el par clave-valor. Además, Este método se usa generalmente en el método equals() como una de sus principales formas de identificar objetos. equals() es un método de la clase Object cuyo trabajo es comparar objetos y determinar si son iguales. Este método se utiliza en todos los lugares donde necesitamos comparar objetos, porque el operador de comparación estándar == no es adecuado para objetos, ya que solo compara referencias de objetos.

96. ¿Cuéntenos sobre el contrato entre iguales () y hashCode () en Java?

Primero, déjame decirte que para que los métodos equals() y hashCode() funcionen correctamente, deben anularse correctamente. Sus nuevas implementaciones deben seguir estas reglas:
  • Los objetos idénticos para los cuales igual devuelve verdadero deben tener los mismos códigos hash.
  • Los objetos con los mismos códigos hash no son necesariamente iguales.
¡Ahora parece un buen momento para hacer una pausa hasta la siguiente parte de la revisión!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION