Эффективно используйте эту библиотеку инструментов Java, и объем кода можно будет сократить на 50 %.

Используйте эту библиотеку инструментов Java с пользой, и объем кода сразу сократится на 50 %!

Guava — это набор инструментов расширения библиотеки классов Java, разработанный Google, который содержит множество API-интерфейсов, охватывающих коллекции, кэширование, параллелизм, ввод-вывод и многие другие аспекты. С одной стороны, использование этих API может упростить наш код и сделать его более элегантным, с другой — дополнить многие функции, которых нет в jdk, что может сделать нашу разработку более эффективной.

Сегодня я поделюсь с вами некоторыми Mapкокетливыми операциями, инкапсулированными в Guava, После использования этих функций я должен сказать что-то действительно приятное. Сначала введите зависимые координаты, а затем начните наш формальный опыт~

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1.1-jre</version>
</dependency>

Таблица — Двойной ключ Карта

Только один и один Mapразрешены в java , но один может существовать в guava . Две in называются и , то есть строка и столбец соответственно. (Но я лично считаю, что не очень точно понимать их как строки и столбцы. Возможно, более уместно рассматривать их как два столбца)keyvalueTablevaluekeyTablekeyrowKeycolumnKey

Чтобы привести простой пример, если вы хотите записать количество дней, которые сотрудник работает каждый месяц. Если вы используете обычную Mapреализацию в java, вам нужны два уровня вложенности:

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);

//Получение элементов
Integer dayCount = map.get("Hydra").get("Jan");

Это очень просто , если вы его используете Table, взгляните на упрощенный код:

Table<String,String,Integer> table= HashBasedTable.create();
//存放元素
table.put("Hydra", "Jan", 20);
table.put("Hydra", "Feb", 28);

table.put("Стволы", "Ян", 28);
table.put("Стволы", "Фев", 16);

//Получить элементы
Integer dayCount = table.get("Hydra", "Feb");

Нам больше не нужно строить сложный двухслойный слой Map, мы можем сделать это прямо в одном слое. Помимо доступа к элементам, рассмотрим другие практические операции.

1. Получите набор ключа или значения

//rowKey或columnKey的集合
Set<String> rowKeys = table.rowKeySet();
Set<String> columnKeys = table.columnKeySet();

//valueCollection
<Integer> values ​​= table.values();

Выведите их результаты отдельно, keyколлекция не содержит повторяющихся элементов и valueколлекция содержит все элементы без дедупликации:

[Hydra, Trunks]
[Jan, Feb]
[20, 28, 28, 16]

2. Подсчитать сумму всех значений, соответствующих ключу

В качестве примера возьмем сумму всех rowKeyсоответствующих статистик:value

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);
}

Распечатать результат:

Hydra: 48
Trunks: 44

3. Преобразование rowKey и columnKey

Эту операцию также можно понимать как перестановку строк и столбцов, Tablesстатический метод , вызываемый напрямую 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())
);

Используйте cellSetметод, чтобы получить все строки данных, распечатать результаты, и вы увидите, rowчто columnобмен произошел:

Jan,Hydra:20
Feb,Hydra:28
Jan,Trunks:28
Feb,Trunks:16

4. Преобразование во вложенную карту

Вы все еще помните формат, в котором мы Tableхраним данные перед использованием? Если вы хотите восстановить данные во вложенной Mapформе, вы можете использовать для Tableэтого метод or rowMap:columnMap

Map<String, Map<String, Integer>> rowMap = table.rowMap();
Map<String, Map<String, Integer>> columnMap = table.columnMap();

Просмотрите содержимое в преобразованном формате Map, которое суммируется по строке и столбцу:

{Hydra={Jan=20, Feb=28}, Trunks={Jan=28, Feb=16}}
{Jan={Hydra=20, Trunks=28}, Feb={Hydra=28, Trunks=16}}

BiMap — двунаправленная карта

В общем Map, если вы хотите найти valueсоответствующий объект key, нет простого пути.Используете ли вы forцикл или итератор, вам нужно пройти весь объект Map. Например, в цикле 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;
}

Однако Guava BiMapпредоставляет структуру данных, связанную keyс valueдвумя направлениями.Давайте рассмотрим простой пример:

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"));

Результаты:

IronMan
Thanos

Выглядит очень полезно, не так ли? Тем не менее, есть еще несколько подводных камней, которых следует избегать при использовании, которые будут разобраны ниже.

1. Влияние операции после обращения

Выше мы использовали inverseметод для реверсирования исходного BiMapсопоставления ключ-значение, но этот реверсированный объект BiMapне является новым объектом, он реализует ассоциацию представлений, поэтому BiMapвсе операции, выполняемые с реверсивным, будут действовать на исходный BiMapвышестоящий.

HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("Hydra","Programmer");
biMap.put("Tony","IronMan");
biMap.put("Thanos","Titan");
BiMap<String, String> inverse = biMap.inverse();

inverse.put("Железный Человек","Старк");
System.out.println(biMap);

BiMapПосле изменения содержимого в инвертированном BiMapснова посмотрите на исходное содержимое:

{Hydra=Programmer, Thanos=Titan, Stark=IronMan}

IronManВидно, что ключ, соответствующий исходному значению Tony, хотя и не подвергался непосредственному изменению, но теперь ключ изменился Stark.

2. Значение не может повторяться

BiMapЛежащее в основе наследование Map, мы знаем, что дублирование не разрешено Mapв , а двусторонняя нейтрализация может считаться находящейся в эквивалентном положении, поэтому на этом основании добавляются ограничения, а дублирование не допускается. Взгляните на код ниже:keyBiMapkeyvaluevalue

HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("Tony","IronMan");
biMap.put("Stark","IronMan");

Таким образом, код не может завершиться нормально, и IllegalArgumentExceptionбудет выброшено исключение:

Если вы действительно хотите сопоставить новый keyс существующим value, вы также можете использовать forcePutметод для принудительной замены исходного key:

HashBiMap<String, String> biMap = HashBiMap.create();
biMap.put("Tony","IronMan");
biMap.forcePut("Stark","IronMan");

Распечатайте замененное BiMap:

{Stark=IronMan}

Кстати, поскольку BiMapон valueне допускает повторения, его valuesметод возвращает не повторение Set, а обычный Collection:

Set<String> values = biMap.values();

Multimap — многозначная карта

Java Mapподдерживает отношение ключ-значение один к одному. Если вы хотите сопоставить ключ с несколькими значениями, вы можете установить содержимое значения только как коллекцию. Простая реализация выглядит следующим образом:

Map<String, List<Integer>> map=new HashMap<>();
List<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
map.put("day",list);

Guava Multimapпредоставляет форму сопоставления ключа с несколькими значениями. Ему не нужно определять сложные внутренние коллекции. Вы можете использовать его как обычный. MapОпределите и поместите данные следующим образом:

Multimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.put("day",1);
multimap.put("day",2);
multimap.put("day",8);
multimap.put("month",3);

Распечатайте Multimapсодержимое этого, и вы сможете интуитивно увидеть, что каждое keyсоответствующее является коллекцией:

{month=[3], day=[1, 2, 8]}

1. Получите набор значений

В приведенной выше операции созданный обычный метод Multimapвернет get(key)коллекцию Collectionтипа:

Collection<Integer> day = multimap.get("day");

Если он указан как ArrayListMultimapтип во время создания, getметод вернет List:

ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
List<Integer> day = multimap.get("day");

Точно так же можно создавать и HashMultimapт.п. TreeMultimapтипы Multimap.

MultimapМетод getвернет не null-коллекцию, но содержимое этой коллекции может быть пустым, взгляните на следующий пример:

List<Integer> day = multimap.get("day");
List<Integer> year = multimap.get("year");
System.out.println(day);
System.out.println(year);

Распечатать результат:

[1, 2, 8]
[]

2. Сбор после операции получить

Подобно BiMapиспользованию , getколлекция, возвращаемая методом, не является независимым объектом. Ее можно понимать как ассоциацию представления коллекции. Операция над этой новой коллекцией по-прежнему будет воздействовать на исходную . Взгляните на Multimapследующее пример:

ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.put("day",1);
multimap.put("day",2);
multimap.put("day",8);
multimap.put("month",3);

Список<Целое> день = multimap.get("день");
Список<Целое> месяц = ​​multimap.get("месяц");

day.remove(0);//Этот 0 является индексом
month.add(12);
System.out.println(multimap);

Проверьте измененные результаты:

{month=[3, 12], day=[2, 8]}

3. Преобразовать в карту

С помощью asMapметода его можно Multimapпреобразовать в Map<K,Collection>форму, и это Mapтоже можно рассматривать как связанное представление, и Mapоперация над этим будет действовать на исходное 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);

Результаты:

month : [3]
day : [1, 2, 8]
{month=[3], day=[1, 2, 8, 20]}

4. Проблемы с количеством

MultimapЧисло in также несколько сбивает с толку при использовании, сначала взгляните на следующий пример:

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());
}

Распечатать результат:

4
4
month,3
day,1
day,2
day,8

Это связано с тем, что size()метод возвращает все сопоставления keyк одному value, поэтому результат равен 4, entries()а метод тот же, возвращая набор пар ключ-значение keyс одним . valueНо он keySetхранит другое keyчисло, например, результат, напечатанный следующей строкой кода, будет 2.

System.out.println(multimap.keySet().size());

Затем посмотрите на преобразование его в Map, сумма изменится:

Set<Map.Entry<String, Collection<Integer>>> entries = multimap.asMap().entrySet();
System.out.println(entries.size());

Результат выполнения кода равен 2, потому что он получил отношение отображения keyк Collection.

RangeMap - Карта диапазона

Давайте сначала рассмотрим пример, предположим, что мы хотим классифицировать результаты тестов на основе результатов, тогда в коде будет что-то уродливое 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;
}

Описание в гуаве RangeMapописывает отношение отображения интервала к определенному значению, что позволяет нам писать код более элегантным способом. Давайте RangeMapизменим приведенный выше код и протестируем его:

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));

В приведенном выше коде [0,60)закрытые слева и открытые справа интервалы, [60,90]закрытые интервалы, а также (90,100]открытые слева и закрытые справа интервалы создаются последовательно и сопоставляются с определенными значениями соответственно. Текущий результат печатает:

fail
satisfactory
satisfactory
excellent

[70,80]Конечно, мы также можем удалить раздел пробела.После того, как следующий код удалит этот замкнутый интервал, getпри повторном выполнении будет возвращен результат null:

rangeMap.remove(Range.closed(70,80));
System.out.println(rangeMap.get(75));

ClassToInstanceMap — карта экземпляра

ClassToInstanceMapявляется специальным Map, его ключом Classи значением является Classсоответствующий объект экземпляра. Давайте сначала рассмотрим простой пример использования putInstanceметодов для хранения объектов:

ClassToInstanceMap<Object> instanceMap = MutableClassToInstanceMap.create();
User user=new User("Hydra",18);
Dept dept=new Dept("develop",200);

instanceMap.putInstance(Пользователь.класс,пользователь);
instanceMap.putInstance(Отдел.класс,отдел);

Используйте getInstanceметод для получения объекта:

User user1 = instanceMap.getInstance(User.class);
System.out.println(user==user1);

Результат операции печатается true, указывая, что объект, который был извлечен, действительно является объектом, который мы создали и вставили ранее.

Вы можете задаться вопросом, если вы просто сохраняете объекты, Map<Class,Object>вы также можете использовать обычные объекты, например:

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);

Итак, ClassToInstanceMapкаковы преимущества использования этого подхода?

Во-первых, самое очевидное здесь то, что сложное обязательное приведение типов при извлечении объекта опускается, а также избегается ошибка ручного приведения типов. Во-вторых, мы можем взглянуть на ClassToInstanceMapопределение интерфейса, которое является общим:

public interface ClassToInstanceMap<B> extends Map<Class<? extends B>, B>{...}

Этот универсальный тип также может играть роль в ограничении типа. valueЧтобы соответствовать keyсоответствующему типу, взгляните на следующий пример:

ClassToInstanceMap<Map> instanceMap = MutableClassToInstanceMap.create();
HashMap<String, Object> hashMap = new HashMap<>();
TreeMap<String, Object> treeMap = new TreeMap<>();
ArrayList<Object> list = new ArrayList<>();

instanceMap.putInstance(HashMap.class,hashMap);
instanceMap.putInstance(TreeMap.class,treeMap);

Это может быть выполнено обычным образом, потому что HashMapи TreeMapоба интегрируют Mapродительский класс, но если вы хотите поместить другие типы, вы скомпилируете и сообщите об ошибке.

Итак, если вы хотите кэшировать объекты, но не хотите делать сложную проверку типов, то можете использовать что угодно ClassToInstanceMap.

Подведем итог

В этой статье представлены 5 видов Mapрасширенных структур данных в гуаве, которые предоставляют очень практичные функции и могут значительно упростить наш код. Но в то же время есть много ловушек, которых нужно избегать, например, изменение связанного представления повлияет на исходные данные и т. д. Вы должны быть более осторожными в конкретном использовании.

Guess you like

Origin blog.csdn.net/dedede001/article/details/130681637