Si estás leyendo este artículo, es probable que estés familiarizado con la interfaz del Mapa y sus usos. Si no, aquí tienes. Hoy hablaremos sobre las características de implementación de TreeMap y, más específicamente, en qué se diferencia de HashMap y cómo usarlo correctamente.
Como puedes ver, estas clases tienen mucho en común, pero también hay algunas diferencias. Aunque la clase
La búsqueda del elemento requerido comienza desde la raíz del árbol, en nuestro caso es 61. A continuación se realiza una comparación con el valor requerido. Si nuestro valor es menor vamos a la izquierda, si es mayor vamos a la derecha. Esto sucede hasta que encontramos el valor requerido o llegamos a un elemento con un valor
Comparación de TreeMap, HashMap y LinkedHashMap
La implementación más utilizada de la interfaz Map es HashMap . Es fácil de usar y garantiza un acceso rápido a los datos, lo que lo convierte en el mejor candidato para la mayoría de las tareas. La mayoría, pero no todos. A veces es necesario almacenar datos de forma estructurada con la capacidad de navegar a través de ellos. En este caso, otra implementación de la interfaz Map viene al rescate : TreeMap . TreeMap implementa la interfaz NavigableMap , que hereda de SortedMap , que a su vez hereda de la interfaz Map . Al implementar las interfaces NavigableMap y SortedMap , TreeMap obtiene una funcionalidad adicional que HashMap no tiene , pero esto tiene un precio en términos de rendimiento. También hay una clase LinkedHashMap , que también le permite almacenar datos en un orden determinado, en el orden en que se agregaron. Para ayudarle a comprender las diferencias entre estas tres clases, consulte esta tabla:HashMap | LinkedHashMap | ÁrbolMapa | |
---|---|---|---|
Orden de almacenamiento de datos | Aleatorio. No hay garantías de que el orden se mantendrá en el tiempo. | En orden de suma | En orden ascendente o en función de un comparador determinado |
Tiempo de acceso al elemento | O(1) | O(1) | O(log(n)) |
Interfaces implementadas | Mapa | Mapa | Mapa navegableMapa ordenadoMapa |
Implementación basada en estructura de datos. | cubos | cubos | Árbol rojo-negro |
Capacidad para trabajar con claves nulas. | Poder | Poder | Es posible si usa un comparador que permita nulo |
A salvo de amenazas | No | No | No |
TreeMap
es la más multifuncional, no siempre se puede almacenar null
como clave. Además, el tiempo de acceso a los elementos de TreeMap será el mayor. Por lo tanto, si no necesita almacenar datos ordenados, es mejor utilizar HashMap
o LinkedHashMap
.
Árbol de ébano rojo
Como probablemente habrás notado, bajo el capóTreeMap
utiliza una estructura de datos llamada árbol rojo-negro. Es el almacenamiento de datos en esta estructura lo que garantiza el orden en el que se almacenan los datos. ¿Qué es este árbol? ¡Vamos a resolverlo! Imagine que necesita almacenar pares de números y cadenas. Los números 16, 20, 52, 55, 61, 65, 71, 76, 81, 85, 90, 93, 101 serán las claves. Si almacena datos en una lista tradicional y necesita encontrar un elemento con la clave 101, deberá recorrer los 13 elementos para encontrarlo. Para 13 elementos esto no es crítico; cuando trabajemos con un millón tendremos grandes problemas. Para resolver estos problemas, los programadores utilizan estructuras de datos un poco más complejas. ¡Por lo tanto, conoce el árbol rojo-negro!
https://algorithmtutor.com/Data-Structures/Tree/Red-Black-Trees/
null
(una hoja de un árbol). Los colores rojo y negro se utilizan para hacer que el árbol sea más fácil de navegar y equilibrar. Hay reglas que siempre se deben seguir al construir un árbol rojo-negro:
- La raíz debe ser de color negro.
- Las hojas del árbol deben ser negras.
- Un nodo rojo debe tener dos nodos secundarios negros.
- Un nodo negro puede tener nodos secundarios.
- El camino desde un nodo hasta sus hojas debe contener la misma cantidad de nodos negros.
- Se agregan nuevos nodos en lugar de hojas.
TreeMap
.
Métodos derivados de las interfaces SortedMap y NavigableMap
Implementa la interfazHashMap
, lo que significa que tiene todos los métodos que . Pero además, implementa interfaces y , obteniendo de ellas funcionalidad adicional. - una interfaz que amplía y agrega métodos relevantes para un conjunto de datos ordenados: TreeMap
Map
TreeMap
HashMap
TreeMap
SortedMap
NavigableMap
SortedMap
Map
firstKey()
: devuelve la clave del primer elemento del mapa;lastKey()
: devuelve la clave del último elemento;headMap(K end)
: devuelve un mapa que contiene todos los elementos del actual, desde el principio hasta el elemento con la claveend
;tailMap(K start)
: devuelve un mapa que contiene todos los elementos del actual, comenzando con el elementostart
y terminando con el final;subMap(K start, K end)
: devuelve un mapa que contiene todos los elementos del actual, comenzando con el elementostart
y terminando con el elemento con la claveend
.
NavigableMap
— una interfaz que amplía SortedMap
y agrega métodos para navegar entre elementos del mapa:
firstEntry()
: devuelve el primer par clave-valor;lastEntry()
: devuelve el último par clave-valor;pollFirstEntry()
: devuelve y elimina el primer par;pollLastEntry()
: devuelve y elimina el último par;ceilingKey(K obj)
: Devuelve la clave más pequeña k que es mayor o igual que la clave obj. Si no existe dicha clave, devuelve nulo;floorKey(K obj)
: Devuelve la clave k más grande que es menor o igual que la clave obj. Si no existe dicha clave, devuelve nulo;lowerKey(K obj)
: Devuelve la clave k más grande que es más pequeña que la clave obj. Si no existe dicha clave, devuelve nulo;higherKey(K obj)
: Devuelve la clave más pequeña k que es mayor que la clave obj. Si no existe dicha clave, devuelve nulo;ceilingEntry(K obj)
: similar al método techoKey(K obj), pero devuelve un par clave-valor (o nulo);floorEntry(K obj)
: similar al método FloorKey(K obj), pero devuelve un par clave-valor (o nulo);lowerEntry(K obj)
: similar al método lowerKey(K obj), pero devuelve un par clave-valor (o nulo);higherEntry(K obj)
: similar al método lowerKey(K obj), pero devuelve un par clave-valor (o nulo);descendingKeySet()
: devuelve un NavigableSet que contiene todas las claves ordenadas en orden inverso;descendingMap()
: devuelve un NavigableMap que contiene todos los pares ordenados en orden inverso;navigableKeySet()
: devuelve un objeto NavigableSet que contiene todas las claves en orden de almacenamiento;headMap(K upperBound, boolean incl)
: devuelve un mapa que contiene pares desde el principio hasta el elemento límite superior. El argumento incl especifica si el elemento UpperBound debe incluirse en el mapa devuelto;tailMap(K lowerBound, boolean incl)
: la funcionalidad es similar al método anterior, solo se devuelven los pares desde el límite inferior hasta el final;subMap(K lowerBound, boolean lowIncl, K upperBound, boolean highIncl)
: Como en los métodos anteriores, se devuelven pares desde límite inferior hasta límite superior, y los argumentos lowIncl y highIncl indican si se deben incluir los elementos de límite en el nuevo mapa.
TreeMap
, además de los constructores que conocemos, se agrega uno más que acepta una instancia del comparador. Este comparador será responsable del orden en el que se almacenen los elementos.
Ejemplos de uso de TreeMap
Tal abundancia de métodos adicionales puede parecer innecesaria, pero muy a menudo resultan mucho más útiles de lo que parecía inicialmente. Veamos este ejemplo contigo. Imaginemos que trabajamos en el departamento de marketing de una gran empresa y tenemos una base de datos de personas a las que queremos mostrar publicidad. Hay dos matices en esto:- necesitamos realizar un seguimiento del número de impresiones de cada persona;
- El algoritmo para mostrar publicidad a menores es diferente.
Person
que almacenará toda la información disponible para nosotros sobre una persona:
public class Person {
public String firstName;
public String lastName;
public int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}
Implementamos la lógica en la clase Main
:
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
TreeMap<Person, Integer> map = new TreeMap<>(Comparator.comparingInt(o -> o.age));
map.put(new Person("John", "Smith", 17), 0);
map.put(new Person("Ivan", "Petrenko", 65), 0);
map.put(new Person("Pedro", "Escobar", 32), 0);
map.put(new Person("Radion", "Pyatkin", 14), 0);
map.put(new Person("Sergey", "Vashkevich", 19), 0);
Person firstAdultPerson = map.navigableKeySet().stream().filter(person -> person.age>18).findFirst().get();
Map<Person, Integer> youngPeopleMap = map.headMap(firstAdultPerson, false);
Map<Person, Integer> adultPeopleMap = map.tailMap(firstAdultPerson, true);
showAdvertisementToYoung(youngPeopleMap);
showAdvertisementToAdult(adultPeopleMap);
}
public static void showAdvertisementToYoung(Map map){}
public static void showAdvertisementToAdult(Map map){}
}
En la clase Main
que creamos TreeMap
, donde key
es una persona específica y value
es el número de impresiones de anuncios este mes. En el constructor pasamos un comparador que ordenará a las personas por edad. Rellenar con map
valores aleatorios. Ahora necesitamos obtener una referencia al primer adulto en nuestro mini almacén de datos. Hacemos esto usando la API Stream. Después de esto, obtenemos dos mapas independientes, que pasamos a los métodos que muestran publicidad. Hay muchas, muchas maneras en que se podría resolver este problema. El arsenal de métodos de la clase TreeMap
le permite inventar soluciones para todos los gustos. No es necesario recordarlos todos, porque siempre puedes utilizar la documentación o sugerencias del entorno de desarrollo. ¡Eso es todo! Espero que la clase ya TreeMap
te resulte clara y que encuentres una aplicación precisa para resolver problemas prácticos.
GO TO FULL VERSION