¡Haga un buen uso de esta biblioteca de herramientas Java y la cantidad de código se reducirá directamente en un 50%!
Guava es un kit de herramientas de extensión de la biblioteca de clases de Java desarrollado por Google, que contiene una gran cantidad de API, que cubre colecciones, almacenamiento en caché, concurrencia, E/S y muchos otros aspectos. Por un lado, el uso de estas API puede simplificar nuestro código y hacerlo más elegante, por otro lado, complementa muchas funciones que no están en jdk, lo que puede hacer que nuestro desarrollo sea más eficiente.
Lo que les compartiré hoy son algunas de Map
las operaciones coquetas encapsuladas en Guava, después de usar estas funciones, tengo que decir algo muy dulce. Primero introduzca las coordenadas dependientes y luego comience nuestra experiencia formal~
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
Tabla - Mapa de doble llave
Solo uno y uno están Map
permitidos en java , pero uno puede existir en guayaba . Los dos en se denominan y , es decir, fila y columna, respectivamente. (Pero personalmente siento que no es muy exacto entenderlos como filas y columnas. Puede ser más apropiado considerarlos como dos columnas)key
value
Table
value
key
Table
key
rowKey
columnKey
Para dar un ejemplo simple, si desea registrar la cantidad de días que trabaja un empleado cada mes. Si usa la Map
implementación ordinaria en Java, necesita dos niveles de anidamiento:
Map<String,Map<String,Integer>> map=new HashMap<>();
//存放元素
Map<String,Integer> workMap=new HashMap<>();
workMap.put("Jan",20);
workMap.put("Feb",28);
map.put("Hydra",workMap);
//Recuperar elementos
Integer dayCount = map.get("Hydra").get("Jan");
Es muy sencillo si lo usas Table
, échale un vistazo al código simplificado:
Table<String,String,Integer> table= HashBasedTable.create();
//存放元素
table.put("Hydra", "Jan", 20);
table.put("Hydra", "Feb", 28);
table.put("Troncos", "Ene", 28);
table.put("Troncos", "Feb", 16);
//Recuperar elementos
Integer dayCount = table.get("Hydra", "Feb");
Ya no necesitamos construir dos capas complicadas Map
, podemos hacerlo directamente en una capa. Además del acceso a los elementos, veamos otras operaciones prácticas.
1. Obtener el conjunto de clave o valor
//rowKey或columnKey的集合
Set<String> rowKeys = table.rowKeySet();
Set<String> columnKeys = table.columnKeySet();
//valor集合
Colección<Integer> valores = table.values();
Imprima sus resultados por separado, key
la colección no contiene elementos duplicados y value
la colección contiene todos los elementos sin deduplicación:
[Hydra, Trunks]
[Jan, Feb]
[20, 28, 28, 16]
2. Calcular la suma de todos los valores correspondientes a la clave
Tome la suma de todas las estadísticas rowKey
correspondientes value
como ejemplo:
for (String key : table.rowKeySet()) {
Set<Map.Entry<String, Integer>> rows = table.row(key).entrySet();
int total = 0;
for (Map.Entry<String, Integer> row : rows) {
total += row.getValue();
}
System.out.println(key + ": " + total);
}
Imprimir resultado:
Hydra: 48
Trunks: 44
3. Convertir clave de fila y clave de columna
Esta operación también puede entenderse como la transposición de filas y columnas, Tables
un método estático llamado directamente transpose
:
Table<String, String, Integer> table2 = Tables.transpose(table);
Set<Table.Cell<String, String, Integer>> cells = table2.cellSet();
cells.forEach(cell->
System.out.println(cell.getRowKey()+","+cell.getColumnKey()+":"+cell.getValue())
);
Use cellSet
el método para obtener todas las filas de datos, imprima los resultados y podrá ver row
que column
se ha producido el intercambio:
Jan,Hydra:20
Feb,Hydra:28
Jan,Trunks:28
Feb,Trunks:16
4. Convertir a un mapa anidado
¿Aún recuerda el formato en el que almacenamos Table
los datos antes de usarlos? Si desea restaurar los datos a la Map
forma anidada, puede usar el método o para Table
lograrlo rowMap
:columnMap
Map<String, Map<String, Integer>> rowMap = table.rowMap();
Map<String, Map<String, Integer>> columnMap = table.columnMap();
Vea el contenido en el formato convertido Map
, que se resume por fila y columna:
{Hydra={Jan=20, Feb=28}, Trunks={Jan=28, Feb=16}}
{Jan={Hydra=20, Trunks=28}, Feb={Hydra=28, Trunks=16}}
BiMap - Mapa bidireccional
En general Map
, si desea buscar value
el objeto correspondiente key
, no hay una manera fácil. Ya sea que use for
un bucle o un iterador, debe recorrer todo el objeto Map
. Por ejemplo, en un bucle keySet
:
public List<String> findKey(Map<String, String> map, String val){
List<String> keys=new ArrayList<>();
for (String key : map.keySet()) {
if (map.get(key).equals(val))
keys.add(key);
}
return keys;
}
Sin embargo, Guava BiMap
proporciona una estructura de datos asociada key
con value
dos direcciones. Veamos un ejemplo simple:
HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("Hydra","Programmer");
biMap.put("Tony","IronMan");
biMap.put("Thanos","Titan");
//使用key获取value
System.out.println(biMap.get("Tony"));
BiMap<String, String> inverse = biMap.inverse();
//使用value获取key
System.out.println(inverse.get("Titan"));
Resultados de la,:
IronMan
Thanos
Parece muy útil, ¿no? Sin embargo, todavía hay algunos escollos que evitar en el uso, que se resolverán uno por uno a continuación.
1. El impacto de la operación tras la reversión
Arriba usamos inverse
el método para revertir el BiMap
mapeo original de clave-valor, pero este objeto invertido BiMap
no es un objeto nuevo, implementa una asociación de vista, por lo que BiMap
todas las operaciones realizadas en el invertido actuarán sobre el original BiMap
superior.
HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("Hydra","Programmer");
biMap.put("Tony","IronMan");
biMap.put("Thanos","Titan");
BiMap<String, String> inverse = biMap.inverse();
inversa.put("IronMan","Stark");
Sistema.salida.println(biMap);
BiMap
Después de modificar el contenido en el invertido , mira BiMap
de nuevo el contenido original:
{Hydra=Programmer, Thanos=Titan, Stark=IronMan}
IronMan
Se puede ver que la clave correspondiente al valor original es Tony
, aunque no se ha modificado directamente, pero ahora la clave ha cambiado Stark
.
2. El valor no se puede repetir
BiMap
La herencia subyacente de Map
, sabemos que la duplicación no está permitida Map
en , y se puede considerar que la neutralización bidireccional está en una posición equivalente, por lo que se agregan restricciones sobre esta base y no se permite la duplicación. Echa un vistazo al código a continuación:key
BiMap
key
value
value
HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("Tony","IronMan");
biMap.put("Stark","IronMan");
De esta forma, el código no puede terminar normalmente y IllegalArgumentException
se lanzará una excepción:
Si realmente desea asignar el nuevo key
al existente value
, también puede usar forcePut
el método para forzar el reemplazo del original key
:
HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("Tony","IronMan");
biMap.forcePut("Stark","IronMan");
Imprime el reemplazado BiMap
:
{Stark=IronMan}
Por cierto, dado que BiMap
no value
permite la repetición, su values
método no devuelve repetición Set
, no ordinario Collection
:
Set<String> values = biMap.values();
Multimap - Mapa multivalor
Java Map
mantiene una relación clave-valor de uno a uno. Si desea asignar una clave a varios valores, solo puede establecer el contenido del valor como una colección. La implementación simple es la siguiente:
Map<String, List<Integer>> map=new HashMap<>();
List<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
map.put("day",list);
Guava Multimap
proporciona una forma de asignar una clave a múltiples valores. No necesita definir colecciones internas complejas. Puede usarlo como uno normal. Map
Defina y coloque los datos de la siguiente manera:
Multimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.put("day",1);
multimap.put("day",2);
multimap.put("day",8);
multimap.put("month",3);
Imprime Multimap
el contenido de esto, y podrás ver intuitivamente que cada key
correspondiente es una colección:
{month=[3], day=[1, 2, 8]}
1. Obtenga una colección de valores
En la operación anterior, el método normal creado Multimap
devolverá get(key)
una Collection
colección de tipo:
Collection<Integer> day = multimap.get("day");
Si se especifica como ArrayListMultimap
un tipo en el momento de la creación, get
el método devolverá un List
:
ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
List<Integer> day = multimap.get("day");
De la misma manera, también puede crear tipos HashMultimap
, etc.TreeMultimap
Multimap
Multimap
El get
método devolverá una no null
colección, pero el contenido de esta colección puede estar vacío, observe el siguiente ejemplo:
List<Integer> day = multimap.get("day");
List<Integer> year = multimap.get("year");
System.out.println(day);
System.out.println(year);
Imprimir resultado:
[1, 2, 8]
[]
2. La colección después de la operación get
Similar al BiMap
uso de , get
la colección devuelta por el método no es un objeto independiente. Puede entenderse como la asociación de la vista de colección. La operación en esta nueva colección seguirá actuando en la original Multimap
. Eche un vistazo a lo siguiente ejemplo:
ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.put("day",1);
multimap.put("day",2);
multimap.put("day",8);
multimap.put("month",3);
List<Integer> dia = multimap.get("dia");
List<Integer> mes = multimap.get("mes");
day.remove(0);//Este 0 es el subíndice
month.add(12);
System.out.println(multimap);
Mira los resultados modificados:
{month=[3, 12], day=[2, 8]}
3. Convertir a Mapa
Usando asMap
el método, se puede Multimap
convertir al Map<K,Collection>
formulario, y esto Map
también se puede considerar como una vista asociada, y Map
la operación en esto actuará en el original Multimap
.
Map<String, Collection<Integer>> map = multimap.asMap();
for (String key : map.keySet()) {
System.out.println(key+" : "+map.get(key));
}
map.get("day").add(20);
System.out.println(multimap);
Resultados de la:
month : [3]
day : [1, 2, 8]
{month=[3], day=[1, 2, 8, 20]}
4. Problemas de cantidad
Multimap
El número en también es algo confuso en su uso, primero mire el siguiente ejemplo:
System.out.println(multimap.size());
System.out.println(multimap.entries().size());
for (Map.Entry<String, Integer> entry : multimap.entries()) {
System.out.println(entry.getKey()+","+entry.getValue());
}
Imprimir resultado:
4
4
month,3
day,1
day,2
day,8
Esto se debe a que size()
el método devuelve todas las asignaciones key
a una sola value
, por lo que el resultado es 4, entries()
y el método es el mismo, devolviendo una colección de pares clave-valor key
con una sola . value
Pero keySet
almacena un número diferente key
, por ejemplo, el resultado impreso por la siguiente línea de código será 2.
System.out.println(multimap.keySet().size());
Luego mire convertirlo a Map
, la cantidad cambia:
Set<Map.Entry<String, Collection<Integer>>> entries = multimap.asMap().entrySet();
System.out.println(entries.size());
El resultado de la ejecución del código es 2, porque obtuvo la relación de asignación key
a Collection
.
RangeMap - Mapa de rango
Primero veamos un ejemplo, suponiendo que queremos clasificar los puntajes de las pruebas en función de los puntajes, entonces habrá algo desagradable como esto en el código if-else
:
public static String getRank(int score){
if (0<=score && score<60)
return "fail";
else if (60<=score && score<=90)
return "satisfactory";
else if (90<score && score<=100)
return "excellent";
return null;
}
La descripción en guayaba RangeMap
describe una relación de mapeo de un intervalo a un valor específico, lo que nos permite escribir código de una manera más elegante. Modifiquemos RangeMap
el código anterior y probemos:
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closedOpen(0,60),"fail");
rangeMap.put(Range.closed(60,90),"satisfactory");
rangeMap.put(Range.openClosed(90,100),"excellent");
System.out.println(rangeMap.get(59));
System.out.println(rangeMap.get(60));
System.out.println(rangeMap.get(90));
System.out.println(rangeMap.get(91));
En el código anterior, [0,60)
los intervalos cerrados a la izquierda y abiertos a la derecha, [60,90]
los intervalos cerrados y (90,100]
los intervalos abiertos a la izquierda y cerrados a la derecha se crean sucesivamente y se asignan a ciertos valores respectivamente. El resultado de la ejecución imprime:
fail
satisfactory
satisfactory
excellent
[70,80]
Por supuesto, también podemos eliminar una sección de espacio.Después de que el siguiente código elimina este intervalo cerrado, get
el resultado devuelto cuando se ejecuta de nuevo es null
:
rangeMap.remove(Range.closed(70,80));
System.out.println(rangeMap.get(75));
ClassToInstanceMap - Mapa de instancias
ClassToInstanceMap
es especial Map
, su clave es Class
y el valor es el Class
objeto de instancia correspondiente. Primero veamos un ejemplo simple del uso de putInstance
métodos para almacenar objetos:
ClassToInstanceMap<Object> instanceMap = MutableClassToInstanceMap.create();
User user=new User("Hydra",18);
Dept dept=new Dept("develop",200);
instanciaMap.putInstance(Usuario.clase,usuario);
instanceMap.putInstance(Depto.clase,depto);
Utilice getInstance
el método para recuperar el objeto:
User user1 = instanceMap.getInstance(User.class);
System.out.println(user==user1);
Se imprime el resultado de la operación true
, indicando que el objeto que se sacó es efectivamente el objeto que creamos y pusimos antes.
Quizás se pregunte, si solo guarda objetos, Map<Class,Object>
también puede usar objetos ordinarios como este:
Map<Class,Object> map=new HashMap<>();
User user=new User("Hydra",18);
Dept dept=new Dept("develop",200);
map.put(User.class,user);
map.put(Dept.class,dept);
Entonces, ClassToInstanceMap
¿cuáles son los beneficios de usar este enfoque?
En primer lugar, lo más obvio aquí es que se omite la compleja conversión de tipo obligatoria al sacar el objeto, y se evita el error de conversión de tipo manual. En segundo lugar, podemos ver ClassToInstanceMap
la definición de la interfaz, que es genérica:
public interface ClassToInstanceMap<B> extends Map<Class<? extends B>, B>{...}
Este genérico también puede desempeñar un papel en la restricción del tipo. value
Para ajustarse al key
tipo correspondiente, eche un vistazo al siguiente ejemplo:
ClassToInstanceMap<Map> instanceMap = MutableClassToInstanceMap.create();
HashMap<String, Object> hashMap = new HashMap<>();
TreeMap<String, Object> treeMap = new TreeMap<>();
ArrayList<Object> list = new ArrayList<>();
instanciaMap.putInstance(HashMap.class,hashMap);
instanciaMapa.putInstance(TreeMap.class,treeMap);
Esto se puede ejecutar normalmente, porque HashMap
ambos TreeMap
integran Map
la clase principal, pero si desea agregar otros tipos, compilará e informará un error.
Por lo tanto, si desea almacenar objetos en caché, pero no desea realizar una verificación de tipo compleja, puede usar lo que sea conveniente ClassToInstanceMap
.
Resumir
Este artículo presenta 5 tipos Map
de estructuras de datos extendidas en guava, que brindan funciones muy prácticas y pueden simplificar enormemente nuestro código. Pero al mismo tiempo, hay muchas trampas que deben evitarse, como modificar la vista asociada que afectará los datos originales, etc. Debe ser más cauteloso en el uso específico.