JavaRush /Blog Java /Random-ES /La historia de una entrevista: preguntas interesantes.
GuitarFactor
Nivel 30
Санкт-Петербург

La historia de una entrevista: preguntas interesantes.

Publicado en el grupo Random-ES
Recientemente tuve la oportunidad de asistir a una entrevista para un puesto de prácticas en una de las grandes empresas de TI. La historia de una entrevista: preguntas interesantes - 1Esta fue mi primera entrevista de TI y, en mi opinión, resultó interesante. En total, me “interrogaron” durante más de 3 horas (antes de esto había tareas y un examen en la oficina en una computadora). Quiero rendir homenaje al entrevistador, que no se rindió cuando respondí incorrectamente la pregunta, pero con la ayuda de sus preguntas capciosas me obligó a pensar y llegar a la respuesta correcta. A continuación presentaré varios “bocetos”; en mi opinión, preguntas bastante interesantes, algunas de las cuales me permitieron comprender mejor ciertos aspectos de Java. Quizás estas cosas parezcan obvias para algunos, pero creo que habrá quienes esto será útil. Debajo, las frases están resaltadas en las siguientes fuentes: Entrevistador: en negrita Explicaciones en off y mis pensamientos: en cursiva Mis respuestas: en fuente normal Hemos terminado con los antecedentes, vayamos al grano)

Bosquejo 1. “Un método aparentemente sencillo”

Escribe cómo implementarías un método que devuelva el resultado de dividir el número a por el número b. El entrevistador escribe en una hoja de papel
int divide(int a, int b) {
}
*Miré con incredulidad el trozo de papel con la firma del método. ¿Cuál es el problema?* Escribo:
int divide(int a, int b) {
    return a/b;
}
¿Hay algún problema con este método? *Estoy atrapando a un imbécil realmente estúpido* Aparentemente no... Luego viene una pregunta legítima: ¿Qué pasa si b=0? *¡Vaya, estoy a punto de ser expulsado de esta oficina si sigo así!* Oh, sí, por supuesto. Aquí tenemos argumentos de tipo int, por lo que se generará una excepción aritmética. Si los argumentos fueran de tipo float o double, el resultado sería Infinity. ¿Qué vamos a hacer al respecto? Estoy empezando a escribir try/catch.
int divide(int a, int b) {
    try {
        return a/b;
    } catch (Exception e) {
        e.printStackTrace();
        return ... // ??? what the hack?
    }
}
*Puedo regresar y congelar: es necesario devolver algo en caso de error. Pero ¿cómo distinguir este “algo” del resultado del cálculo?* ¿ Qué devolveremos? Hm... cambiaría el tipo de variable de retorno a Entero y en caso de una excepción devolvería nulo. Imaginemos que no podemos cambiar el tipo. ¿Podremos salir de alguna manera? ¿Quizás podamos hacer algo más con la excepción? *Aquí viene* ¡ También podemos reenviarlo al método de llamada! Bien. ¿Como se verá esto?
int divide(int a, int b) throws ArithmeticException{
    return a/b;
}

void callDivide(int a, int b) {
    try {
        divide(a, b);
    } catch (ArithmeticException e) {
        e.printStackTrace();
    }
}
¿Es necesario manejar la excepción? Sí, porque lo reenviamos explícitamente desde el método de división. (*¡Me equivoqué aquí! Lo que sigue son preguntas importantes del entrevistador para llegar a la respuesta correcta*) Y la excepción aritmética: ¿qué tipo de excepción es? ¿Marcada o no marcada? Esta es una excepción en tiempo de ejecución, lo que significa que no está marcada. *Aquí viene la pregunta clave* Entonces, en tus palabras, resulta que si especificamos throws Arithmetic Exception en la firma del método, ¿entonces se convirtió en una excepción marcada? *¡Uf!* Probablemente... no. Sí, ya no está. Si indicamos throws /excepción no marcada/ en la firma, solo advertimos que el método puede lanzar una excepción, pero no es necesario manejarla en el método que llama. Eso está solucionado. ¿Hay algo más que podamos hacer para evitar errores? *Después de pensarlo un poco* Sí, también podemos comprobar si (b==0). Y realizar algo de lógica. Bien. Entonces podemos ir de 3 maneras:
  • trata de atraparlo
  • throws – reenvío al método de llamada
  • comprobación de argumentos
En este caso, divide¿qué método crees que es preferible?
Elegiría reenviar la excepción al método de llamada, porque... en el método de división no está claro cómo procesar esta excepción y qué tipo de resultado intdevolver en caso de error. Y en el método de llamada, usaría el argumento b para comprobar si es igual a cero. Parece que esta respuesta satisfizo al entrevistado, pero para ser honesto, no estoy seguro de que esta respuesta sea inequívoca))

Bosquejo 2. "¿Quién es más rápido?"

Después de la pregunta estándar, ¿en qué se diferencia una ArrayList de una LinkedList?, surgió esto: ¿ Qué sucederá más rápido: insertar un elemento en el medio ArrayListo en el medio LinkedList? *Aquí salté, recordé que en todos lados leía algo así como “usar LinkedListpara insertar o quitar elementos en el medio de la lista”. En casa incluso revisé dos veces las conferencias de JavaRush, hay una frase: “si vas a insertar (o eliminar) muchos elementos en medio de una colección, entonces será mejor que uses LinkedList. En todos los demás casos ArrayList... Respondido automáticamente* Será más rápido con LinkedList. aclarar por favor
  1. Para insertar un elemento en el medio ArrayList, buscamos el elemento en la lista en tiempo constante, y luego recalculamos los índices de los elementos a la derecha del insertado, en tiempo lineal.
  2. Para LinkedList... Primero llegamos al medio en tiempo lineal y luego insertamos un elemento en tiempo constante, cambiando enlaces para elementos vecinos.
Entonces resulta, ¿cuál es más rápido? Hm... Resulta lo mismo. ¿ Pero cuándo es LinkedListmás rápido? Resulta que cuando lo insertamos en la primera mitad de la lista. Por ejemplo, si lo insertas desde el principio, ArrayListtendrás que recalcular todos los índices hasta el final, pero LinkedListsolo tendrás que cambiar la referencia del primer elemento. Moraleja: ¡no creas literalmente todo lo que está escrito, ni siquiera en JavaRush!)

Bosquejo 3. “¿Dónde estaríamos sin iguales y sin código hash?”

La conversación sobre iguales y código hash fue muy larga: cómo anularlo, qué implementación Object, qué sucede bajo el capó, cuándo se inserta un elemento en HashMap, etc. Me limitaré a citar una serie de puntos que, en mi opinión, son interesantes* Imaginemos que hemos creado una clase
public class A {
    int id;

    public A(int id) {
        this.id = id;
    }
}
Y no anularon equalsy hashcode. Describe lo que sucederá cuando se ejecute el código.
A a1 = new A(1);
A a2 = new A(1);
Map<A, String> hash = new HashMap<>();
hash.put(a1, "1");
hash.get(a2);
*Es bueno que antes de la entrevista haya dedicado específicamente un par de días a comprender los algoritmos básicos, su complejidad y estructuras de datos; me ayudó mucho, ¡gracias CS50!*
  1. Crea dos instancias de clase A.

  2. Creamos un mapa vacío, que por defecto tiene 16 cestas. La clave es un objeto de clase A, en el que los métodos equalsy no se anulan hashcode.

  3. Ponlo a1en el mapa. Para ello, primero calculamos el hash a1.

    ¿A qué será igual el hash?

    La dirección de una celda en la memoria es una implementación de un método de una clase.Object

  4. Basándonos en el hash, calculamos el índice de la cesta.

    ¿Cómo podemos calcularlo?

    *Desafortunadamente, no di una respuesta clara aquí. Tiene un número largo (un hash y hay 16 depósitos). ¿Cómo definir un índice para que los objetos con diferentes hashes se distribuyan uniformemente entre los depósitos? Me imagino que el índice se calcula así:

    int index = hash % buckets.length

    Ya en casa vi que la implementación original en el código fuente es ligeramente diferente:

    static int indexFor(int h, int length)
    {
        return h & (length - 1);
    }
  5. Comprobamos que no hay colisiones e insertamos a1.

  6. Pasemos al método get. Se garantiza que las instancias a1 y a2 tendrán una hashdirección diferente (diferente en la memoria), por lo que no encontraremos nada para esta clave.

    ¿Qué pasa si lo redefinimos solo hashcodeen la clase A e intentamos insertar en el mapa hash primero un par con la clave a1 y luego con a2?

    Luego, primero encontraremos la canasta deseada hashcode; esta operación se realizará correctamente. A continuación, comencemos a revisar los objetos Entryen LinkedList adjunto al carrito y comparemos las claves por equals. Porque equalsno se anula, entonces la implementación base se toma de la clase Object: comparación por referencia. Se garantiza que a1 y a2 tendrán enlaces diferentes, por lo que "perderemos" el elemento insertado a1 y a2 se colocará en LinkedList como un nuevo nodo.

    ¿Cuál es la conclusión? ¿Es posible utilizarlo como clave en HashMapun objeto sin anular equalshashcode?

    No, no puedes.

Boceto 4. “¡Rompámoslo a propósito!”

Después de las preguntas sobre Error y Excepción, siguió la siguiente pregunta: Escriba un ejemplo simple donde una función lanzará StackOverflow. *Entonces recordé cómo me atormentaba este error cuando intentaba escribir alguna función recursiva* Esto probablemente sucederá en el caso de una llamada recursiva, si la condición para salir de la recursión se especifica incorrectamente. *Entonces comencé a ponerme un poco astuto, al final el entrevistador me ayudó, todo resultó sencillo*
void sof() {
    sof();
}
¿ En qué se diferencia este error OutOfMemory? *No respondí aquí, solo más tarde me di cuenta de que se trataba de una pregunta sobre el conocimiento Stackde Heapla memoria Java (las llamadas y referencias a objetos se almacenan en la pila, y los objetos mismos se almacenan en la memoria del montón). En consecuencia, StackOverflow se descarta cuando no hay más espacio en Stackla memoria para la siguiente llamada al método y OutOfMemoryel espacio para los objetos se ha agotado en Heapla memoria*
Estos son los momentos de la entrevista que recuerdo. Al final me aceptaron para unas prácticas, así que tengo por delante 2,5 meses de formación y, si todo va bien, un trabajo en la empresa) Si hay interés, puedo escribir otro artículo, esta vez más pequeño, con un análisis de un problema simple pero ilustrativo que me dieron una entrevista en otra empresa. Eso es todo para mí, espero que este artículo ayude a alguien a profundizar u organizar sus conocimientos. ¡Feliz aprendizaje a todos!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION