1. Обзор
В этом уроке мы сравним две библиотеки с открытым исходным кодом на основе Java: Apache Commons и Google Guava . Обе библиотеки имеют богатый набор функций с большим количеством служебных API, в основном в области коллекций и ввода-вывода.
Для краткости здесь мы опишем только несколько наиболее часто используемых из фреймворка коллекций вместе с образцами кода. Мы также увидим краткое изложение их различий.
Кроме того, у нас есть коллекция статей для глубокого погружения в различные commons и Guava утилиты .
2. Краткая история двух библиотек
Google Guava-это проект Google, в основном разработанный инженерами организации, хотя в настоящее время он является открытым исходным кодом. Основной мотивацией для его запуска было включение дженериков , введенных в JDK 1.5 , в Java Collections Framework или JCF и расширение его возможностей.
С момента своего создания библиотека расширила свои возможности и теперь включает в себя графики, функциональное программирование, объекты диапазона, кэширование и Строковые манипуляции.
Apache Commons начинался как проект в Джакарте, дополняющий API основных коллекций Java, и в конечном итоге стал проектом Apache Software Foundation. С годами он расширился до обширного репертуара повторно используемых компонентов Java в различных других областях, включая (но не ограничиваясь) визуализацию, ввод-вывод, криптографию, кэширование, создание сетей, проверку и объединение объектов.
Поскольку это проект с открытым исходным кодом, разработчики из сообщества Apache продолжают добавлять в эту библиотеку, чтобы расширить ее возможности. Тем не менее, они очень заботятся о поддержании обратной совместимости .
3. Зависимость Maven
Чтобы включить гуаву, нам нужно добавить ее зависимость в ваш pom.xml :
com.google.guava guava 29.0-jre
Информацию о последней версии можно найти на Maven .
Для Apache Commons все немного по-другому. В зависимости от утилиты, которую мы хотим использовать, мы должны добавить именно эту. Например, для коллекций нам нужно добавить:
org.apache.commons commons-collections4 4.4
В наших примерах кода мы будем использовать commons-collections4 .
Давайте перейдем к самой веселой части прямо сейчас!
4. Двунаправленные карты
Карты, к которым можно получить доступ с помощью их ключей, а также значений, называются двунаправленными картами. JCF не имеет этой функции.
Давайте посмотрим, как наши две технологии предлагают их. В обоих случаях мы возьмем пример дней недели, чтобы получить название дня с учетом его номера, и наоборот.
4.1. Бимап гуавы
Guava предлагает интерфейс – BiMap , в виде двунаправленной карты. Он может быть создан с помощью одной из его реализаций EnumBiMap , EnumHashBiMap , HashBiMap или ImmutableBiMap .
Здесь мы используем HashBiMap :
BiMapdaysOfWeek = HashBiMap.create();
Население похоже на любую карту на Java:
daysOfWeek.put(1, "Monday"); daysOfWeek.put(2, "Tuesday"); daysOfWeek.put(3, "Wednesday"); daysOfWeek.put(4, "Thursday"); daysOfWeek.put(5, "Friday"); daysOfWeek.put(6, "Saturday"); daysOfWeek.put(7, "Sunday");
И вот несколько тестов JUnit, чтобы доказать эту концепцию:
@Test public void givenBiMap_whenValue_thenKeyReturned() { assertEquals(Integer.valueOf(7), daysOfWeek.inverse().get("Sunday")); } @Test public void givenBiMap_whenKey_thenValueReturned() { assertEquals("Tuesday", daysOfWeek.get(2)); }
4.2. БидиМап Apache
Аналогично, Apache предоставляет нам свой интерфейс BidiMap:
BidiMapdaysOfWeek = new TreeBidiMap ();
Здесь мы используем TreeBidiMap . Однако существуют и другие реализации, такие как DualHashBidiMap и DualTreeBidiMap .
Чтобы заполнить его, мы можем поместить значения, как мы сделали для Bitmap выше.
Его использование также довольно похоже:
@Test public void givenBidiMap_whenValue_thenKeyReturned() { assertEquals(Integer.valueOf(7), daysOfWeek.inverseBidiMap().get("Sunday")); } @Test public void givenBidiMap_whenKey_thenValueReturned() { assertEquals("Tuesday", daysOfWeek.get(2)); }
В нескольких простых тестах производительности эта двунаправленная карта отставала от своего аналога Гуавы только во вставках. Это было намного быстрее при извлечении ключей, а также значений .
5. Сопоставьте ключи с несколькими значениями
Для случая использования, когда мы хотим сопоставить несколько ключей с различными значениями, например, коллекцию продуктовых корзин для фруктов и овощей, две библиотеки предлагают нам уникальные решения.
5.1. Мультикарта гуавы
Во-первых, давайте посмотрим, как создать экземпляр и инициализировать MultiMap:
MultimapgroceryCart = ArrayListMultimap.create(); groceryCart.put("Fruits", "Apple"); groceryCart.put("Fruits", "Grapes"); groceryCart.put("Fruits", "Strawberries"); groceryCart.put("Vegetables", "Spinach"); groceryCart.put("Vegetables", "Cabbage");
Затем мы используем пару тестов JUnit, чтобы увидеть его в действии:
@Test public void givenMultiValuedMap_whenFruitsFetched_thenFruitsReturned() { Listfruits = Arrays.asList("Apple", "Grapes", "Strawberries"); assertEquals(fruits, groceryCart.get("Fruits")); } @Test public void givenMultiValuedMap_whenVeggiesFetched_thenVeggiesReturned() { List veggies = Arrays.asList("Spinach", "Cabbage"); assertEquals(veggies, groceryCart.get("Vegetables")); }
Кроме того, MultiMap дает нам возможность удалить данную запись или весь набор значений с карты :
@Test public void givenMultiValuedMap_whenFuitsRemoved_thenVeggiesPreserved() { assertEquals(5, groceryCart.size()); groceryCart.remove("Fruits", "Apple"); assertEquals(4, groceryCart.size()); groceryCart.removeAll("Fruits"); assertEquals(2, groceryCart.size()); }
Как мы видим, здесь мы сначала удалили Apple из набора Fruits , а затем удалили весь набор Fruits .
5.2. Многозначная карта Apache
Опять же, давайте начнем с создания экземпляра многозначной карты:
MultiValuedMapgroceryCart = new ArrayListValuedHashMap<>();
Поскольку население это то же самое, что мы видели в предыдущем разделе, давайте быстро рассмотрим использование:
@Test public void givenMultiValuedMap_whenFruitsFetched_thenFruitsReturned() { Listfruits = Arrays.asList("Apple", "Grapes", "Strawberries"); assertEquals(fruits, groceryCart.get("Fruits")); } @Test public void givenMultiValuedMap_whenVeggiesFetched_thenVeggiesReturned() { List veggies = Arrays.asList("Spinach", "Cabbage"); assertEquals(veggies, groceryCart.get("Vegetables")); }
Как мы видим, его использование также одинаково!
Однако в этом случае у нас нет возможности удалить одну запись, например Apple из Fruits. Мы можем удалить только весь набор Фруктов :
@Test public void givenMultiValuedMap_whenFuitsRemoved_thenVeggiesPreserved() { assertEquals(5, groceryCart.size()); groceryCart.remove("Fruits"); assertEquals(2, groceryCart.size()); }
6. Сопоставьте несколько ключей с одним значением
Здесь мы возьмем пример широты и долготы, которые будут нанесены на соответствующие города:
cityCoordinates.put("40.7128° N", "74.0060° W", "New York"); cityCoordinates.put("48.8566° N", "2.3522° E", "Paris"); cityCoordinates.put("19.0760° N", "72.8777° E", "Mumbai");
Теперь мы посмотрим, как этого добиться.
6.1. Стол гуавы
Гуава предлагает свою таблицу, которая удовлетворяет приведенному выше варианту использования:
TablecityCoordinates = HashBasedTable.create();
И вот некоторые обычаи, которые мы можем извлечь из этого:
@Test public void givenCoordinatesTable_whenFetched_thenOK() { List expectedLongitudes = Arrays.asList("74.0060° W", "2.3522° E", "72.8777° E"); assertArrayEquals(expectedLongitudes.toArray(), cityCoordinates.columnKeySet().toArray()); List expectedCities = Arrays.asList("New York", "Paris", "Mumbai"); assertArrayEquals(expectedCities.toArray(), cityCoordinates.values().toArray()); assertTrue(cityCoordinates.rowKeySet().contains("48.8566° N")); }
Как мы видим, мы можем получить Set представление строк, столбцов и значений.
Таблица также предлагает нам возможность запрашивать ее строки или столбцы .
Давайте рассмотрим таблицу фильмов, чтобы продемонстрировать это:
Tablemovies = HashBasedTable.create(); movies.put("Tom Hanks", "Meg Ryan", "You've Got Mail"); movies.put("Tom Hanks", "Catherine Zeta-Jones", "The Terminal"); movies.put("Bradley Cooper", "Lady Gaga", "A Star is Born"); movies.put("Keenu Reaves", "Sandra Bullock", "Speed"); movies.put("Tom Hanks", "Sandra Bullock", "Extremely Loud & Incredibly Close");
И вот несколько простых, понятных поисков, которые мы можем выполнить в нашем фильмах таблице :
@Test public void givenMoviesTable_whenFetched_thenOK() { assertEquals(3, movies.row("Tom Hanks").size()); assertEquals(2, movies.column("Sandra Bullock").size()); assertEquals("A Star is Born", movies.get("Bradley Cooper", "Lady Gaga")); assertTrue(movies.containsValue("Speed")); }
Однако Таблица ограничивает нас сопоставлением только двух ключей со значением . У нас пока нет альтернативы в Гуаве, чтобы сопоставить более двух ключей с одним значением.
6.2. Мультикеймап Apache
Возвращаясь к нашему примеру Координаты города , вот как мы можем манипулировать им с помощью MultiKeyMap :
@Test public void givenCoordinatesMultiKeyMap_whenQueried_thenOK() { MultiKeyMapcityCoordinates = new MultiKeyMap (); // populate with keys and values as shown previously List expectedLongitudes = Arrays.asList("72.8777° E", "2.3522° E", "74.0060° W"); List longitudes = new ArrayList<>(); cityCoordinates.forEach((key, value) -> { longitudes.add(key.getKey(1)); }); assertArrayEquals(expectedLongitudes.toArray(), longitudes.toArray()); List expectedCities = Arrays.asList("Mumbai", "Paris", "New York"); List cities = new ArrayList<>(); cityCoordinates.forEach((key, value) -> { cities.add(value); }); assertArrayEquals(expectedCities.toArray(), cities.toArray()); }
Как мы видим из приведенного выше фрагмента кода, чтобы получить те же утверждения , что и для таблицы Гуавы , нам пришлось перебирать MultiKeyMap .
Однако MultiKeyMap также предлагает возможность сопоставить более двух ключей со значением . Например, это дает нам возможность отображать дни недели как будни или выходные:
@Test public void givenDaysMultiKeyMap_whenFetched_thenOK() { days = new MultiKeyMap(); days.put("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Weekday"); days.put("Saturday", "Sunday", "Weekend"); assertFalse(days.get("Saturday", "Sunday").equals("Weekday")); }
7. Коллекции Apache Commons vs. Гугл Гуава
По словам его инженеров , Google Guava родилась из-за необходимости использовать дженерики в библиотеке, которые Apache Commons не предлагал . Он также соответствует требованиям API коллекций к тройнику. Еще одним важным преимуществом является то, что он находится в активной разработке с частыми выпусками новых версий.
Тем не менее, Apache предлагает преимущество, когда дело доходит до производительности при извлечении значения из коллекции. Гуава по-прежнему берет торт, хотя с точки зрения времени вставки.
Хотя мы сравнили только API-интерфейсы коллекций в наших примерах кода, Apache Commons в целом предлагает гораздо большую гамму функций по сравнению с Guava .
8. Заключение
В этом уроке мы сравнили некоторые функции, предлагаемые Apache Commons и Google Guava, особенно в области фреймворка коллекций.
Здесь мы просто поцарапали поверхность того, что могут предложить эти две библиотеки.
Более того, это не сравнение “или-или”. Как показали наши примеры кода, существуют функции, уникальные для каждого из них, и могут возникнуть ситуации, когда оба могут сосуществовать .
Как всегда, исходный код доступен на GitHub .