Advertisement
detectivejd

HashMap - implementación propia

Apr 30th, 2017
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 25.24 KB | None | 0 0
  1. package hashmapsimple;
  2. import java.util.AbstractCollection;
  3. import java.util.AbstractSet;
  4. import java.util.Arrays;
  5. import java.util.Collection;
  6. import java.util.Iterator;
  7. import java.util.Map;
  8. import java.util.Set;
  9. /**
  10.  * Hacer una implementación propia de HashMap ha sido una experiencia algo
  11.  * desafiante ya que el HashMap que hace Oracle y desde versiones antes de la
  12.  * 7 dicha estructura depende ciegamente de la función hashCode para que su
  13.  * funcionamiento sea óptimo, pero al investigar un poco usando funciones de
  14.  * búsqueda secuencial como indexPut y getIndex hace cuestionar un poco la
  15.  * existencia de la función hashCode, además de decir que querían un estilo
  16.  * diferente para obtener las posiciones para almacenar o buscar mediante
  17.  * fórmulas matemáticas.
  18.  * Por otro lado esta temática de hacer distintos Iterators basándose en uno
  19.  * abstracto y hacer que su comportamiento sea distinto en base a su clase
  20.  * abstracta es algo ingenioso de parte de Oracle, aunque algunas condiciones
  21.  * y métodos que usé para lograr dicha implementación son un poco cuestionados
  22.  * porque no se me ocurrió una idea mejor de cómo implementarlos.
  23.  * El reto sería implementar un HashSet "casero" y que funcione internamente
  24.  * por medio de este HashMap "creado"
  25.  *
  26.  * @author detectivejd
  27.  * @param <K>
  28.  * @param <V>
  29.  */
  30. public class MyMap<K, V> implements Map<K,V>{
  31.     private int capacity;
  32.     private int size;
  33.     private Entry<K,V>[] table;
  34.     /**
  35.      * Contructor del HashMap
  36.      */
  37.     public MyMap() {
  38.         this(4);
  39.     }
  40.     /**
  41.      * Constructor del HashMap con disponibilidad de decir que cantidad
  42.      * de elementos deseamos almacenar
  43.      *
  44.      * @param xcapacity  
  45.      */
  46.     public MyMap(int xcapacity) {
  47.         if(xcapacity <= 0){
  48.             throw new IllegalArgumentException("Capacidad no permitida: " + capacity);
  49.         } else {
  50.             this.capacity = xcapacity;        
  51.         }
  52.         this.clear();
  53.     }
  54.     /**
  55.      * Constructor del HashMap que utilizamos para almacenar toda una estructura
  56.      * de datos map a nuestra estructura de datos
  57.      *
  58.      * @param m
  59.      */
  60.     public MyMap(Map<? extends K, ? extends V> m) {
  61.         this.putAll(m);
  62.     }
  63.     /**
  64.      * Método utilizado para vaciar la estructura de datos
  65.      */
  66.     @Override
  67.     public void clear() {
  68.         /*
  69.             inicializo el array de las entradas según la capacidad
  70.             puesta por defecto o pasada en el constructor
  71.         */
  72.         table = new Entry[capacity];
  73.         // inicializo la cantidad de elementos a 0
  74.         size = 0;
  75.     }
  76.     /**
  77.      * Método utilizado para transferir toda una estructura de tipo
  78.      * Map a nuestro HashMap "casero"
  79.      *
  80.      * @param m -> mapa de clave/valor
  81.      */
  82.     @Override
  83.     public void putAll(Map<? extends K, ? extends V> m) {
  84.         /*
  85.             Si la cantidad del Map a transferir es mayor a 0,
  86.             recorremos dicho Map y por cada entrada que tenga
  87.             el mismo será almacenado a nuestro "HashMap"
  88.         */
  89.         if(m.size() > 0){
  90.             m.entrySet().forEach((e) -> {
  91.                 this.put(e.getKey(), e.getValue());
  92.             });
  93.         }
  94.     }
  95.     /**
  96.      * Función que utilizamos para guardar una entrada clave/valor
  97.      * a nuestro HashMap "casero"
  98.      *
  99.      * @param key -> clave
  100.      * @param value -> valor
  101.      * @return V -> valor
  102.      */
  103.     @Override
  104.     public V put(K key, V value) {
  105.         // si la clave es nula, la función retorna nula
  106.         if(key == null){
  107.             return null;
  108.         } else {
  109.             /*
  110.                 de lo contrario creamos una variable entera llamada index a la
  111.                 cual le asigno como valor el resultado de la función getIndex
  112.                 pasándole nuestra clave como parámetro, dicha función devuelve
  113.                 el índice de una entrada de datos existente.
  114.             */
  115.             int index = this.getIndex(key);
  116.             /*
  117.                 si index es distinto a -1 es porque encontró una entrada en
  118.                 nuestra estructura de datos. Sería lo mismo que decir si es != null
  119.             */
  120.             if(index != -1){
  121.                 /*
  122.                     Luego preguntamos si la entrada del índice encontrado cuya
  123.                     clave es igual a la clave que pasamos por parámetro en la
  124.                     función
  125.                 */
  126.                 if (table[index].getKey().equals(key)) {
  127.                     /*
  128.                         creamos una variable tipo Value llamada oldValue
  129.                         al cual le pasamos el valor actual de la entrada
  130.                         cuyo índice fue encontrado
  131.                     */
  132.                     V oldValue = table[index].getValue();
  133.                     /*
  134.                         reemplazamos el valor actual de la entrada encontrada
  135.                         por el valor pasado por parámetro
  136.                     */
  137.                     table[index].setValue(value);
  138.                     /*
  139.                         retornamos en la función la variable oldValue dando
  140.                         nuestra función finalizada en caso de que la clave pasada
  141.                         por parámetro ya existe en una de las entradas almacenadas
  142.                     */
  143.                     return oldValue;
  144.                 }  
  145.             }
  146.             /*
  147.                 Pero en caso de que la clave pasada por parámetro no exista en
  148.                 nuestra estructura, utilizamos el método addEntry para almacenar
  149.                 la clave y valor pasados por parámetro en nuestro Map
  150.             */
  151.             this.addEntry(key, value);
  152.             /*
  153.                 retornamos en nuestra función el valor pasado por parámetro
  154.                 dando finalizada nuestra función
  155.             */
  156.             return value;
  157.         }
  158.     }
  159.     /**
  160.      * Método privado para almacenar la clave/valor a nuestra
  161.      * estructura de datos
  162.      *
  163.      * @param key -> clave
  164.      * @param value -> valor
  165.      */
  166.     private void addEntry(K key, V value){
  167.         /*
  168.             si la cantidad almacenada en nuestra estructura de datos es
  169.             igual a la cantidad disponible del array
  170.         */
  171.         if(size == table.length){
  172.             // incrementamos a 1 la capacidad disponible del array
  173.             table = Arrays.copyOf(table, table.length +1);
  174.         }
  175.         /*
  176.             creamos una variable a la cual le asignamos el resultado de
  177.             la función indexPut la cual sirve para obtener un índice en
  178.             dónde pueda posicionar la entrada a creer
  179.         */
  180.         int index = indexPut();
  181.         /*
  182.             Creamos una nueva entrada a la cual le pasamos la clave y valor
  183.             los cuales pasamos por parámetro en nuestra función, luego lo
  184.             posicionamos en la posición disponible para almacenar en el map.
  185.         */
  186.         table[index] = new Entry(key, value);
  187.         // incremenatamos la cantidad de elementos a 1 más
  188.         size++;
  189.     }
  190.     /**
  191.      * Función privada utilizada para obtener un índice disponible
  192.      * cuyo fin será posicionar una entrada de datos nueva
  193.      *
  194.      * @return int -> índice para almacenar una entrada nueva
  195.      */
  196.     private int indexPut() {
  197.         /*
  198.             Recorre el array interno de nuestra estructura y si hay
  199.             una posición que esté vacía la función retornará esa posición
  200.         */
  201.         for(int i= 0 ; i < table.length; i++){
  202.             if(table[i] == null){
  203.                  return i;
  204.             }
  205.         }
  206.         /*
  207.             de lo contrario retornará -1, lo cual nunca pasará
  208.         */
  209.         return -1;
  210.     }    
  211.     /**
  212.      * Funciona privada para obtener el índice de una entrada
  213.      * existente
  214.      *
  215.      * @param key -> clave
  216.      * @return int -> índice para obtener una entrada existente
  217.      */
  218.     private int getIndex(Object key) {
  219.         /*
  220.             recorre el array interno de nuestra estructura y si nuestro
  221.             array en la posición actual es distinto de nulo y si el mismo
  222.             cuya clave es igual a la clave pasada por parámetro, entonces
  223.             la función devolverá dicha posición actual quedando finalizada
  224.         */
  225.         for(int i= 0 ; i < table.length; i++){
  226.             if(table[i] != null && table[i].getKey() == key){
  227.                  return i;
  228.             }
  229.         }
  230.         /*
  231.             de lo contrario, la función devolverá -1 dejando en claro
  232.             que no encontró nada, además -1 es lo mismo que decir null
  233.             pero en enteros.
  234.         */
  235.         return -1;
  236.     }
  237.     /**
  238.      * Función utilizada para encontrar un valor mediante una clave
  239.      * pasada por parámetro
  240.      *
  241.      * @param key -> clave
  242.      * @return V -> valor
  243.      */
  244.     @Override
  245.     public V get(Object key) {
  246.         /*
  247.             Usando un operador ternario preguntamos si el resultado de la función
  248.             getEntry a la cual le pasamos por parámetro una clave es null,
  249.             retornará null, de lo contrario retornará el valor de la función
  250.             getEntry que usamos antes
  251.         */
  252.         return (this.getEntry(key) == null) ? null : this.getEntry(key).getValue();
  253.     }
  254.     /**
  255.      * Función útil para determinar si existe una entrada existente
  256.      * por medio de una clave pasada por parámetro
  257.      *
  258.      * @param key -> clave
  259.      * @return boolean -> verdadero o falso
  260.      */
  261.     @Override
  262.     public boolean containsKey(Object key) {
  263.         /*
  264.             Mediante una prueba lógica hacemos que la función devuelve
  265.             el resultado de la función getEntry pasándole la clave
  266.             por parámetro y que la misma es distinto de null
  267.         */
  268.         return this.getEntry(key) != null;
  269.     }
  270.     /**
  271.      * Función privada que utilizamos para obtener una entrada
  272.      * mediante una clave pasada por parámetro
  273.      *
  274.      * @param key
  275.      * @return Entry<K, V> -> entrada clave/valor
  276.      */
  277.     private Entry<K, V> getEntry(Object key) {
  278.         /*
  279.             creamos una variable entera llamada index a la
  280.             cual le asigno como valor el resultado de la función getIndex
  281.             pasándole nuestra clave como parámetro, dicha función devuelve
  282.             el índice de una entrada de datos existente.
  283.         */
  284.         int index = getIndex(key);
  285.         /*
  286.             si index es distinto a -1 es porque encontró una entrada en
  287.             nuestra estructura de datos. Sería lo mismo que decir si es != null
  288.         */
  289.         if(index != -1){
  290.             /*
  291.                 si nuestro array en la posición encontrada es distinto de nulo y si
  292.                 el mismo cuya clave es igual a la clave pasada por parámetro
  293.             */
  294.             if (table[index] != null && table[index].getKey().equals(key)){
  295.                 /*
  296.                     en caso de que la condición se cumpla la función devolverá
  297.                     el contenido del array de la posición encontrada
  298.                 */
  299.                 return table[index];
  300.             }            
  301.         }
  302.         /*
  303.             y si la primera condición no sé cumplió o sea que no encontró
  304.             nada, la función devolverá null
  305.         */
  306.         return null;
  307.     }
  308.     /**
  309.      * Función útil para determinar si existe una entrada existente
  310.      * por medio de un valor pasado por parámetro
  311.      *
  312.      * @param value -> valor
  313.      * @return boolean -> verdadero o falso
  314.      */
  315.     @Override
  316.     public boolean containsValue(Object value){
  317.         // sí el valor pasado por parámetro es distinto de null
  318.         if(value != null){
  319.             // recorremos las entradas existentes de nuestro array interno
  320.             for (Entry val : table) {
  321.                 /*
  322.                     si valor recorrido actual es distinto de nulo y si el mismo
  323.                     es igual al valor pasado por parámetro
  324.                 */
  325.                 if (val != null && value.equals(val.getValue())){
  326.                     // la función devolverá true o sea verdadero
  327.                     return true;
  328.                 }            
  329.             }
  330.         }
  331.         /*
  332.             pero si ninguna condición se cumple, la función devolverá
  333.             false o sea falso
  334.         */
  335.         return false;      
  336.     }
  337.     /**
  338.      * Devuelve la cantidad de elementos almacenados en nuestra
  339.      * estructura de datos
  340.      *
  341.      * @return int -> entero
  342.      */
  343.     @Override
  344.     public int size() {
  345.         return size;
  346.     }
  347.     /**
  348.      * Función que determina si nuestra estructura de datos esta vacía o no
  349.      *
  350.      * @return boolean -> verdadero o falso
  351.      */
  352.     @Override
  353.     public boolean isEmpty(){
  354.         return size == 0;
  355.     }
  356.     /**
  357.      * Función útil para eliminar un elemento existente por medio de
  358.      * una clave pasada por parámetro
  359.      *
  360.      * @param key
  361.      * @return V -> valor
  362.      */
  363.     @Override
  364.     public V remove(Object key){
  365.         /*
  366.             creamos una variable entera llamada index a la
  367.             cual le asigno como valor el resultado de la función getIndex
  368.             pasándole nuestra clave como parámetro, dicha función devuelve
  369.             el índice de una entrada de datos existente.
  370.         */
  371.         int index = getIndex(key);
  372.         /*
  373.             si index es distinto a -1 es porque encontró una entrada en
  374.             nuestra estructura de datos. Sería lo mismo que decir si es != null
  375.         */
  376.         if(index != -1){
  377.             /*
  378.                 creamos una entrada a la cuál le pasamos por valor el contenido
  379.                 del array en la posición que fue encontrada anteriormente, en
  380.                 realidad la entrada creada la usaremos sólo para que la función
  381.                 retorne el valor de la misma nada más.
  382.             */
  383.             Entry<K,V> e = table[index];
  384.             /*
  385.                 recorremos desde el posición encontrada hasta la cantidad de
  386.                 elementos almacenados de nuestra estructura
  387.             */
  388.             for(; index < size; index++){
  389.                 /*
  390.                     mediante los recorridos que hagamos reemplazaremos el
  391.                     contenido de la posición actual por el de la próxima
  392.                     posición, sólo qye tuve que usar un operador ternario
  393.                     debido a que cómo tenía: table[index] = table[index +1];
  394.                     sólo me modificaba hasta la penúltima posición y no la
  395.                     última, hasta el momento encontré esta forma para llevar
  396.                     a cabo mi cometido
  397.                 */
  398.                 table[index] = ((index +1) == size) ? null : table[index + 1];                
  399.             }
  400.             // disminuímos la cantidad almacenada de elementos
  401.             size--;
  402.             /*
  403.                 la función retorna el valor de nuestra entrada que antes
  404.                 habíamos creado
  405.             */
  406.             return e.getValue();                            
  407.         }
  408.         /*
  409.             pero si no cumple para nada la condición del principio, la función
  410.             devolverá null debido a que no encontró entrada con la clave que
  411.             pasamos por parámetro
  412.         */
  413.         return null;
  414.     }  
  415.     /*-----------------------------------------------------------*/
  416.     /**
  417.      * Devuelve un conjunto de todas las claves almacenadas en
  418.      * nuestra estructura de datos
  419.      *
  420.      * @return Set<K> -> tupla de claves
  421.      */
  422.     @Override
  423.     public Set<K> keySet() {
  424.         /*
  425.             devolvemos a esta función una nueva instancia de
  426.             KeySet para el recorrido de las claves
  427.         */
  428.         return new KeySet();
  429.     }  
  430.     /*-----------------------------------------------------------*/
  431.     /**
  432.      * KeySet es una clase interna que utilizamos para las iteraciones
  433.      * (recorridos que hacemos con foreach) de las claves
  434.      */
  435.     private class KeySet extends AbstractSet<K>{
  436.         /**
  437.          * Función usada para dar estilo al recorrido pertinente
  438.          * o sea para las claves
  439.          *
  440.          * @return Iterator<K> -> recorrido de claves
  441.          */
  442.         @Override
  443.         public Iterator<K> iterator() {
  444.             /*
  445.                 Retornamos en la función una nueva instancia de la clase
  446.                 KeyIterator para dar estilo al recorrido de las claves
  447.             */
  448.             return new KeyIterator();
  449.         }
  450.         /**
  451.          * Misma idea de la función size de nuestra estructura
  452.          *
  453.          * @return int -> entero
  454.          */
  455.         @Override
  456.         public int size() {
  457.             return size;
  458.         }        
  459.     }
  460.     /**
  461.      * Clase interna para dar estilo al recorrido de las claves
  462.      */
  463.     private final class KeyIterator extends HashIterator<K> {
  464.         /**
  465.          * Obtiene la siguiente clave del recorrido
  466.          *
  467.          * @return K -> clave
  468.          */
  469.         @Override
  470.         public K next() {
  471.             return nextEntry().getKey();
  472.         }        
  473.     }
  474.     /*-----------------------------------------------------------*/
  475.     /**
  476.      * Devuelve una colección de los valores almacenados de
  477.      * nuestra estructura de datos
  478.      *
  479.      * @return Collection<V> -> colección de valores
  480.      */
  481.     @Override
  482.     public Collection<V> values() {
  483.         /*
  484.             devolvemos a esta función una nueva instancia de
  485.             Values para el recorrido de los valores
  486.         */
  487.         return new Values();
  488.     }
  489.     /**
  490.      * Values es una clase interna que utilizamos para las iteraciones
  491.      * (recorridos que hacemos con foreach) de los valores
  492.      */
  493.     private class Values extends AbstractCollection<V>{
  494.         @Override
  495.         public Iterator<V> iterator() {
  496.             /*
  497.                 Retornamos en la función una nueva instancia de la clase
  498.                 ValueIterator para dar estilo al recorrido de los valores
  499.             */
  500.             return new ValueIterator();
  501.         }
  502.         /**
  503.          * Misma idea de la función size de nuestra estructura
  504.          *
  505.          * @return int -> entero
  506.          */
  507.         @Override
  508.         public int size() {
  509.             return size;
  510.         }        
  511.     }
  512.     /**
  513.      * Clase interna para dar estilo al recorrido de los valores
  514.      */
  515.     private final class ValueIterator extends HashIterator<V>{
  516.         /**
  517.          * Devuelve el siguiente valor del recorrido
  518.          *
  519.          * @return V -> valor
  520.          */
  521.         @Override
  522.         public V next() {
  523.             return nextEntry().getValue();
  524.         }        
  525.     }
  526.     /*-----------------------------------------------------------*/
  527.     /**
  528.      * Devuelve un conjunto de las entradas almacenadas en
  529.      * nuestra estructura de datos
  530.      *
  531.      * @return Set<Map.Entry<K, V>> -> conjunto de entradas
  532.      */
  533.     @Override
  534.     public Set<Map.Entry<K, V>> entrySet() {
  535.         /*
  536.             devolvemos a esta función una nueva instancia de
  537.             EntrySet para el recorrido de las entradas
  538.         */
  539.         return new EntrySet();
  540.     }
  541.     /**
  542.      * EntrySet es una clase interna que utilizamos para las iteraciones
  543.      * (recorridos que hacemos con foreach) de las entradas
  544.      */
  545.     private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
  546.         @Override
  547.         public Iterator<Map.Entry<K, V>> iterator() {
  548.             /*
  549.                 Retornamos en la función una nueva instancia de la clase
  550.                 EntryIterator para dar estilo al recorrido de las entradas
  551.             */
  552.             return new EntryIterator();
  553.         }
  554.         /**
  555.          * Misma idea de la función size de nuestra estructura
  556.          *
  557.          * @return int -> entero
  558.          */
  559.         @Override
  560.         public int size() {
  561.             return size;
  562.         }        
  563.     }
  564.     /**
  565.      * Clase interna para dar estilo al recorrido de las entradas
  566.      */
  567.     private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
  568.         /**
  569.          * Devuelve la siguiente entrada del recorrido
  570.          *
  571.          * @return V -> valor
  572.          */
  573.         @Override
  574.         public Entry<K, V> next() {
  575.             return nextEntry();
  576.         }        
  577.     }
  578.     /*-----------------------------------------------------------*/
  579.     /**
  580.      * Clase abstracta que usamos para los distintos tipos de
  581.      * recorridos empleados en nuestra estructura de datos
  582.      *
  583.      * @param <E>
  584.      */
  585.     private abstract class HashIterator<E> implements Iterator<E> {
  586.         /*
  587.             index una variable que usamos para los recorrer
  588.             entrada por entrada
  589.         */
  590.         private int index = 0;
  591.         // currEntry es la entrada en la que estamos actualmente parados
  592.         private Entry<K,V> currEntry = null;
  593.         // nextEntry es la entrada que le sigue a la actual
  594.         private Entry<K,V> nextEntry = null;
  595.         /**
  596.          * Constructor del HashIterator
  597.          */
  598.         @SuppressWarnings("empty-statement")
  599.         HashIterator() {
  600.             /*
  601.                 Sí el contenido del array en la posición actual 0 es distinto
  602.                 de null, nextEntry tendrá el valor de la primera entrada
  603.                 almacenada ya que hacemos esto para obtener la primera entrada
  604.             */
  605.             if (table[index] != null){
  606.                 nextEntry = table[index];
  607.             }            
  608.         }
  609.         /**
  610.          * Función booleana que determina si hay o no una siguiente
  611.          * entrada
  612.          *
  613.          * @return boolean -> verdadero o falso
  614.          */
  615.         @Override
  616.         public boolean hasNext() {
  617.             /*
  618.                 retornamos la función con una prueba lógica de sí
  619.                 la próxima entrada es distinta de null o no
  620.             */
  621.             return nextEntry != null;
  622.         }
  623.         /**
  624.          * Función utilizada para obtener la entrada próxima, y también
  625.          * una función sobreexplotada para los recorridos ;)
  626.          *
  627.          * @return Entry<K,V> -> entrada clave/valor
  628.          */
  629.         @SuppressWarnings("empty-statement")
  630.         public Entry<K,V> nextEntry() {
  631.             /*
  632.                 a la entrada actual le pasamos como valor la siguiente para
  633.                 usarla al final de la función
  634.             */          
  635.             currEntry = nextEntry;
  636.             // incrementamos el índice al siguiente
  637.             index++;
  638.             /*
  639.                 tuve que hacer esta condición así porque si sólo le ponía
  640.                 de esta forma: if(table[index] != null) me daba este error:
  641.                 Exception in thread "main" ArrayIndexOutOfBoundsException: 6
  642.                 pero la idea es que si el índice actual es menor al tamaño de
  643.                 la entradas almacenadas y el contenido del array de dicha
  644.                 posición es distinto de nulo
  645.             */
  646.             if (index < size && table[index] != null) {
  647.                 /*
  648.                     le asignamos a la siguiente entrada el contenido del
  649.                     array de la posición en la que estamos parados
  650.                 */
  651.                 nextEntry = table[index];              
  652.             } else {
  653.                 /*
  654.                     de lo contrario le asignamos a la siguiente entrada nulo
  655.                     y recorremos desde el índice actual hasta el tamaño del
  656.                     array, preguntando si el índice en el que estamos parados
  657.                     es distinto de null, siendo le asignamos a la siguiente
  658.                     entrada el contenido del array de la posición en la que
  659.                     estamos parados
  660.                 */
  661.                 nextEntry = null;
  662.                 for (;index < size; index++){
  663.                     if (table[index] != null){
  664.                         nextEntry = table[index];
  665.                     }
  666.                 }
  667.             }
  668.             // y retornamos la entrada actual que mostraría la siguiente
  669.             return currEntry;
  670.         }      
  671.     }  
  672.     /*-----------------------------------------------------------*/
  673.     /**
  674.      * Clase interna para definir las entradas clave/valor que
  675.      * almacenaremos en nuestra estructura de datos
  676.      *
  677.      * @param <K>
  678.      * @param <V>
  679.      */
  680.     class Entry<K,V> implements Map.Entry<K,V>{
  681.         /*
  682.             key es la clave que usaremos para identificar nuestra entrada,
  683.             la cual cuyo valor no se modificará
  684.         */
  685.         final K key;
  686.         /*
  687.             value será el valor que tenga la entrada
  688.         */
  689.         V value;
  690.         Entry(K k, V v) {
  691.             value = v;
  692.             key = k;
  693.         }
  694.         @Override
  695.         public final K getKey() {
  696.             return key;
  697.         }
  698.         @Override
  699.         public final V getValue() {
  700.             return value;
  701.         }        
  702.         @Override
  703.         public V setValue(V v) {
  704.             V val = value;
  705.             value = v;
  706.             return val;
  707.         }
  708.         @Override
  709.         public String toString() {
  710.             return getKey() + " -> " + getValue();
  711.         }
  712.     }
  713. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement