Рубрики
Без рубрики

Коллекции Apache Commons против Google Guava

Изучите функциональность, предлагаемую Apache Commons и Google Guava, особенно в области фреймворка коллекций.

Автор оригинала: baeldung.

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 :

BiMap daysOfWeek = 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:

BidiMap daysOfWeek = 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:

Multimap groceryCart = 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() {
    List fruits = 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

Опять же, давайте начнем с создания экземпляра многозначной карты:

MultiValuedMap groceryCart = new ArrayListValuedHashMap<>();

Поскольку население это то же самое, что мы видели в предыдущем разделе, давайте быстро рассмотрим использование:

@Test
public void givenMultiValuedMap_whenFruitsFetched_thenFruitsReturned() {
    List fruits = 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. Стол гуавы

Гуава предлагает свою таблицу, которая удовлетворяет приведенному выше варианту использования:

Table cityCoordinates = 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 представление строк, столбцов и значений.

Таблица также предлагает нам возможность запрашивать ее строки или столбцы .

Давайте рассмотрим таблицу фильмов, чтобы продемонстрировать это:

Table movies = 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() {
    MultiKeyMap cityCoordinates = 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 .