Статьи

Неизменные коллекции, стиль гуавы

Я предпочитаю использовать неизменяемые классы и коллекции как можно чаще. Я часто использовал методы Коллекции для возврата неизменяемых коллекций, Один недостаток «немодифицируемых» методов заключается в том, что они создают только «представления» структур данных, переданных им, и любые изменения этих базовых структур действительно изменяют содержимое этих «представлений». Другими словами, они не изменяются только при непосредственном использовании, но они все еще могут быть изменены при доступе к их основной коллекции. Guava предоставляет хороший набор «неизменяемых» коллекций, которые являются более неизменными или неизменяемыми. В «неизменяемых» коллекциях Гуавы изменения в структуре данных, используемой для заполнения «неизменяемой» коллекции, НЕ отражаются в содержимом неизменной коллекции, потому что это отдельная и отличная копия данных, а не просто «представление». В этом посте я более подробно рассмотрю эту разницу.

В этой статье я показываю серию фрагментов кода и соответствующие результаты их выполнения, чтобы сравнить стандартные «неизменяемые» коллекции JDK с «неизменяемыми» коллекциями Гуавы. Позже в этом посте я включу весь исходный код класса, который содержит все эти методы, но сначала я покажу им несколько методов, чтобы я мог сосредоточиться на каждом из них.

Неизменяемые и неизменяемые наборы

В следующем листинге кода показаны два метода, один из которых использует Collections.unmodifiableSet (Set) JDK, а другой — ImmutableSet.copyOf (Collection) Guava . Я показываю эти два метода и часть главной функции, которая вызывает эти два метода. Я не показываю методы, которые создают наборы данных, предоставляемых этим методам создания экземпляров, но они будут доступны позже в этом посте в листинге кода, который содержит весь тестовый класс.

Демонстрация ImmutableSet.copyOf (Collection) и Collections.unmodifiableSet (Set)

   /**
    * Demonstrate Guava's ImmutableSet.
    */
   public void demoGuavaImmutableSet()
   {
      printHeader("Guava's ImmutableSet");
      final Set<String> originalStrings = buildUnderlyingSampleSet();
      final ImmutableSet<String> strings = ImmutableSet.copyOf(originalStrings);
      out.println("Immutable Set of Strings: " + strings);
      originalStrings.remove("Java");
      out.println("Original Set of Strings: " + originalStrings);
      out.println("Immutable Set of Strings: " + strings);
   }

   /**
    * Demonstrate JDK's UnmodifiableSet.
    */
   public void demoJdkUnmodifiableSet()
   {
      printHeader("JDK unmodifiableSet");
      final Set<String> originalStrings = buildUnderlyingSampleSet();
      final Set<String> strings = Collections.unmodifiableSet(originalStrings);
      out.println("Unmodifiable Set of Strings: " + strings);
      originalStrings.remove("Java");
      out.println("Original Set of Strings: " + originalStrings);
      out.println("Unmodifiable Set of Strings: " + strings);
   }

   /**
    * Main function to demonstrate Guava's immutable collections support.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final GuavaImmutableCollections me = new GuavaImmutableCollections();

      // Compare JDK UnmodifiableSet to Guava's ImmutableSet
      me.demoJdkUnmodifiableSet();
      me.demoGuavaImmutableSet();
   }

Когда приведенный выше код выполняется, вывод (показанный на следующем снимке экрана) указывает, что удаление строки «Java» из исходного набора удаляет его также из набора «немодифицируемых» JDK, который был создан на основе этого исходного набора. Однако «неизменяемый» набор Guava сохраняет строку «Java» даже в том случае, если в том же самом базовом наборе, на котором он был создан, была удалена строка «Java».

Неизменяемые и неизменяемые списки

Как и в случае с наборами, я сосредоточусь в следующем листинге кода на различии между использованием JDK Collections.unmodifiableList (List) и Guava ImmutableList.copyOf (Collection) .

Демонстрация ImmutableList.copyOf (Collection) и Collections.unmodifiableList (List)

   /**
    * Demonstrate Guava's ImmutableList.
    */
   public void demoGuavaImmutableList()
   {
      printHeader("Guava's ImmutableList");
      final List<String> originalStrings = buildUnderlyingSampleList();
      final ImmutableList<String> strings = ImmutableList.copyOf(originalStrings);
      out.println("Immutable List of Strings: " + strings);
      originalStrings.remove("Groovy");
      out.println("Original List of Strings: " + originalStrings);
      out.println("Immutable List of Strings: " + strings);
   }

   /**
    * Demonstrate JDK's UnmodifiableList.
    */
   public void demoJdkUnmodifiableList()
   {
      printHeader("JDK unmodifiableList");
      final List<String> originalStrings = buildUnderlyingSampleList();
      final List<String> strings = Collections.unmodifiableList(originalStrings);
      out.println("Unmodifiable List of Strings: " + strings);
      originalStrings.remove("Groovy");
      out.println("Original List of Strings: " + originalStrings);
      out.println("Unmodifiable List of Strings: " + strings);
   }

   /**
    * Main function to demonstrate Guava's immutable collections support.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final GuavaImmutableCollections me = new GuavaImmutableCollections();

      // Compare JDK UnmodifiableList to Guava's ImmutableList
      me.demoJdkUnmodifiableList();
      me.demoGuavaImmutableList();

Вывод, показанный на следующем снимке экрана, показывает, что при выполнении вышеприведенного кода «неизменяемый» список Guava не удаляет свой элемент, даже когда это делает исходная коллекция.

Неизменяемые и неизменяемые карты

Хотя гуавы в ImmutableMap есть copyOf (Map) метод , аналогичный тем , которые ранее показано , гуавы в ImmutableSet и ImmutableList , я выбираю использовать другой подход к инстанцировании ImmutableMap при сравнении его к тому , что вернулся из JDK в Collections.unmodifiableMap (Карта) метод. В этом случае я использую « Builder » для создания Guava ImmutableMap. Результат тот же: «неизменяемая» карта Гуавы не меняет своих значений даже при изменении базовой структуры данных, из которой была заполнена неизменная карта Гуавы.

Демонстрация ImmutableMap.builder (). PutAll (Карта) и Collections.unmodifiableMap (Карта)

   /**
    * Demonstrate Guava's ImmutableMap. Uses ImmutableMap.builder().
    */
   public void demoGuavaImmutableMap()
   {
      printHeader("Guava's ImmutableMap");
      final Map<String, String> originalStringsMapping = new HashMap<string, string="">();
      originalStringsMapping.put("D", "Dustin");
      originalStringsMapping.put("G", "Guava");
      originalStringsMapping.put("J", "Java");
      final ImmutableMap<String, String> strings =
         ImmutableMap.<String, String>builder().putAll(originalStringsMapping).build();
      out.println("Immutable Map of Strings: " + strings);
      originalStringsMapping.remove("D");
      out.println("Original Map of Strings: " + originalStringsMapping);
      out.println("Immutable Map of Strings: " + strings);
   }

   /**
    * Demonstrate JDK's UnmodifiableMap.
    */
   public void demoJdkUnmodifiableMap()
   {
      printHeader("JDK unmodifiableMap");
      final Map<String, String> originalStringsMapping = new HashMap<string, string="">();
      originalStringsMapping.put("D", "Dustin");
      originalStringsMapping.put("G", "Guava");
      originalStringsMapping.put("J", "Java");
      final Map<String, String> strings = Collections.unmodifiableMap(originalStringsMapping);
      out.println("Unmodifiable Map of Strings: " + strings);
      originalStringsMapping.remove("D");
      out.println("Original Map of Strings: " + originalStringsMapping);
      out.println("Unmodifiable Map of Strings: " + strings);
   }

   /**
    * Main function to demonstrate Guava's immutable collections support.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final GuavaImmutableCollections me = new GuavaImmutableCollections();

      // Compare JDK unmodifiableMap to Guava's ImmutableMap
      me.demoJdkUnmodifiableMap();
      me.demoGuavaImmutableMap();
   }

Как и в случае с множествами и списками, приведенный выше код при выполнении приводит к выводу (снимок экрана ниже), который показывает, что в то время как «неизменяемая» карта JDK действительно изменяется, если базовая карта модифицирована (элемент удален), «Гуава» неизменяемый «Карта не теряет запись, даже если его оригинальная карта делает.

Строители коллекций Гуавы

Хотя я только продемонстрировал использование компоновщика при создании моего примера неизменяемой карты Гуавы, все неизменные коллекции Гуавы поддерживают компоновщиков. Это показано для некоторых из этих коллекций в следующем листинге кода.

Строители коллекций Гуавы

   /**
    * Demonstrate using Builders to build up Guava immutable collections.
    */
   public void demoGuavaBuilders()
   {
      printHeader("Guava's Builders");

      final ImmutableMap<String, String> languageStrings =
         ImmutableMap.<String, String>builder().put("C", "C++")
                                               .put("F", "Fortran")
                                               .put("G", "Groovy")
                                               .put("J", "Java")
                                               .put("P", "Pascal")
                                               .put("R", "Ruby")
                                               .put("S", "Scala").build();
      out.println("Languages Map: " + languageStrings);

      final ImmutableSet<String> states =
         ImmutableSet.<String>builder().add("Arizona")
                                       .add("Colorado")
                                       .add("Wyoming").build();
      out.println("States: " + states);

      final ImmutableList<String> cities =
         ImmutableList.<String>builder().add("Boston")
                                        .add("Colorado Springs")
                                        .add("Denver")
                                        .add("Fort Collins")
                                        .add("Salt Lake City")
                                        .add("San Francisco")
                                        .add("Toledo").build();
      out.println("Cities: " + cities);

      final ImmutableMultimap<String, String> multimapLanguages =
              ImmutableMultimap.<String, String>builder().put("C", "C")
                                                         .put("C", "C++")
                                                         .put("C", "C#")
                                                         .put("F", "Fortran")
                                                         .put("G", "Groovy")
                                                         .put("J", "Java")
                                                         .put("P", "Pascal")
                                                         .put("P", "Perl")
                                                         .put("P", "PHP")
                                                         .put("P", "Python")
                                                         .put("R", "Ruby")
                                                         .put("S", "Scala").build();
      out.println("Languages: " + multimapLanguages);
   }

   /**
    * Main function to demonstrate Guava's immutable collections support.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final GuavaImmutableCollections me = new GuavaImmutableCollections();

      // Demonstrate using builders to build up Guava Immutable Collections
      me.demoGuavaBuilders();
   }

Другие неизменные коллекции гуавы

Гуава обладает неизменными коллекциями, помимо тех, которые показаны в этом посте ( ImmutableMap , ImmutableSet , ImmutableList и ImmutableMultimap ). Эти другие включают ImmutableListMultimap , ImmutableMultiset , ImmutableBiMap , ImmutableSetMultimap , ImmutableSortedMap и ImmutableSortedSet .

Неизменность или изменчивость элементов не определяется типом коллекции

Неизменяемые коллекции Гуавы часто предпочтительнее «неизменяемых» коллекций JDK, потому что неизменяемые коллекции Гуавы не могут быть изменены, даже когда структура данных, на которой они были впервые созданы, изменяется. Однако ни неизменяемые коллекции JDK, ни неизменяемые коллекции Guava не могут ничего сделать с элементами соответствующих коллекций, которые сами являются изменяемыми. Другими словами, человек может не иметь возможности добавить элемент или удалить элемент из неизменяемой или неизменяемой коллекции, но этот же человек может изменить содержимое любого данного элемента, если этот элемент является изменяемым. В моих примерах выше я использовал элементы String. Поскольку строки по своей природе неизменны, элементы моей коллекции не могут быть изменены. Однако,если бы я использовал классы, которые предоставляют методы набора или другие способы изменения данного объекта, то эти объекты могли бы изменить свое значение независимо от того, хранятся ли они в неизменяемых или неизменяемых коллекциях.

Весь код

Фрагменты кода, показанные выше, вместе с некоторыми вспомогательными методами, которые они вызвали, но не были показаны выше, доступны в следующем листинге кода.

GuavaImmutableCollections.java

package dustin.examples;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import static java.lang.System.out;

import com.google.common.collect.ImmutableSet;
import java.util.*;

/**
 * Class that demonstrates Guava's support of immutable collections.
 * 
 * @author Dustin
 */
public class GuavaImmutableCollections
{
   /**
    * Build a sample set to be used in demonstrations.
    * 
    * @return Sample set of Strings.
    */
   public Set<String> buildUnderlyingSampleSet()
   {
      final Set<String> strings = new HashSet<String>();
      strings.add("Dustin");
      strings.add("Java");
      strings.add("College Football");
      return strings;
   }

   /**
    * Build a sample list to be used in demonstrations.
    * 
    * @return Sample list of Strings.
    */
   public List<String> buildUnderlyingSampleList()
   {
      final List<String> gStrings = new ArrayList<String>();
      gStrings.add("Guava");
      gStrings.add("Groovy");
      gStrings.add("Grails");
      gStrings.add("Gradle");
      gStrings.add("Grape");
      return gStrings;
   }

   /**
    * Demonstrate Guava's ImmutableSet.
    */
   public void demoGuavaImmutableSet()
   {
      printHeader("Guava's ImmutableSet");
      final Set<String> originalStrings = buildUnderlyingSampleSet();
      final ImmutableSet<String> strings = ImmutableSet.copyOf(originalStrings);
      out.println("Immutable Set of Strings: " + strings);
      originalStrings.remove("Java");
      out.println("Original Set of Strings: " + originalStrings);
      out.println("Immutable Set of Strings: " + strings);
   }

   /**
    * Demonstrate JDK's UnmodifiableSet.
    */
   public void demoJdkUnmodifiableSet()
   {
      printHeader("JDK unmodifiableSet");
      final Set<String> originalStrings = buildUnderlyingSampleSet();
      final Set<String> strings = Collections.unmodifiableSet(originalStrings);
      out.println("Unmodifiable Set of Strings: " + strings);
      originalStrings.remove("Java");
      out.println("Original Set of Strings: " + originalStrings);
      out.println("Unmodifiable Set of Strings: " + strings);
   }

   /**
    * Demonstrate Guava's ImmutableList.
    */
   public void demoGuavaImmutableList()
   {
      printHeader("Guava's ImmutableList");
      final List<String> originalStrings = buildUnderlyingSampleList();
      final ImmutableList<String> strings = ImmutableList.copyOf(originalStrings);
      out.println("Immutable List of Strings: " + strings);
      originalStrings.remove("Groovy");
      out.println("Original List of Strings: " + originalStrings);
      out.println("Immutable List of Strings: " + strings);
   }

   /**
    * Demonstrate JDK's UnmodifiableList.
    */
   public void demoJdkUnmodifiableList()
   {
      printHeader("JDK unmodifiableList");
      final List<String> originalStrings = buildUnderlyingSampleList();
      final List<String> strings = Collections.unmodifiableList(originalStrings);
      out.println("Unmodifiable List of Strings: " + strings);
      originalStrings.remove("Groovy");
      out.println("Original List of Strings: " + originalStrings);
      out.println("Unmodifiable List of Strings: " + strings);
   }

   /**
    * Demonstrate Guava's ImmutableMap. Uses ImmutableMap.builder().
    */
   public void demoGuavaImmutableMap()
   {
      printHeader("Guava's ImmutableMap");
      final Map<String, String> originalStringsMapping = new HashMap<String, String>();
      originalStringsMapping.put("D", "Dustin");
      originalStringsMapping.put("G", "Guava");
      originalStringsMapping.put("J", "Java");
      final ImmutableMap<String, String> strings =
         ImmutableMap.<String, String>builder().putAll(originalStringsMapping).build();
      out.println("Immutable Map of Strings: " + strings);
      originalStringsMapping.remove("D");
      out.println("Original Map of Strings: " + originalStringsMapping);
      out.println("Immutable Map of Strings: " + strings);
   }

   /**
    * Demonstrate JDK's UnmodifiableMap.
    */
   public void demoJdkUnmodifiableMap()
   {
      printHeader("JDK unmodifiableMap");
      final Map<String, String> originalStringsMapping = new HashMap<String, String>();
      originalStringsMapping.put("D", "Dustin");
      originalStringsMapping.put("G", "Guava");
      originalStringsMapping.put("J", "Java");
      final Map<String, String> strings = Collections.unmodifiableMap(originalStringsMapping);
      out.println("Unmodifiable Map of Strings: " + strings);
      originalStringsMapping.remove("D");
      out.println("Original Map of Strings: " + originalStringsMapping);
      out.println("Unmodifiable Map of Strings: " + strings);
   }

   /**
    * Demonstrate using Builders to build up Guava immutable collections.
    */
   public void demoGuavaBuilders()
   {
      printHeader("Guava's Builders");

      final ImmutableMap<String, String> languageStrings =
         ImmutableMap.<String, String>builder().put("C", "C++")
                                               .put("F", "Fortran")
                                               .put("G", "Groovy")
                                               .put("J", "Java")
                                               .put("P", "Pascal")
                                               .put("R", "Ruby")
                                               .put("S", "Scala").build();
      out.println("Languages Map: " + languageStrings);

      final ImmutableSet<String> states =
         ImmutableSet.<String>builder().add("Arizona")
                                       .add("Colorado")
                                       .add("Wyoming").build();
      out.println("States: " + states);

      final ImmutableList<String> cities =
         ImmutableList.<String>builder().add("Boston")
                                        .add("Colorado Springs")
                                        .add("Denver")
                                        .add("Fort Collins")
                                        .add("Salt Lake City")
                                        .add("San Francisco")
                                        .add("Toledo").build();
      out.println("Cities: " + cities);

      final ImmutableMultimap<String, String> multimapLanguages =
              ImmutableMultimap.<String, String>builder().put("C", "C")
                                                         .put("C", "C++")
                                                         .put("C", "C#")
                                                         .put("F", "Fortran")
                                                         .put("G", "Groovy")
                                                         .put("J", "Java")
                                                         .put("P", "Pascal")
                                                         .put("P", "Perl")
                                                         .put("P", "PHP")
                                                         .put("P", "Python")
                                                         .put("R", "Ruby")
                                                         .put("S", "Scala").build();
      out.println("Languages: " + multimapLanguages);
   }

   /**
    * Write a separation header to standard output that includes provided header
    * text.
    * 
    * @param headerText Text to be used in separation header.
    */
   public static void printHeader(final String headerText)
   {
      out.println("\n========================================================");
      out.println("== " + headerText);
      out.println("========================================================");
   }

   /**
    * Main function to demonstrate Guava's immutable collections support.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final GuavaImmutableCollections me = new GuavaImmutableCollections();

      // Compare JDK UnmodifiableSet to Guava's ImmutableSet
      me.demoJdkUnmodifiableSet();
      me.demoGuavaImmutableSet();

      // Compare JDK UnmodifiableList to Guava's ImmutableList
      me.demoJdkUnmodifiableList();
      me.demoGuavaImmutableList();

      // Compare JDK unmodifiableMap to Guava's ImmutableMap
      me.demoJdkUnmodifiableMap();
      me.demoGuavaImmutableMap();

      // Demonstrate using builders to build up Guava Immutable Collections
      me.demoGuavaBuilders();
   }
}

Вывод

«Неизменяемые» коллекции Guava часто предпочтительнее аналогичных «неизменяемых» коллекций JDK, предоставляемых классом Collections, поскольку неизменяемые коллекции Guava нельзя изменить даже при изменении исходной структуры данных, на которой они были созданы. Причина различия заключается в том, что «неизменяемые» коллекции JDK являются «представлениями» базовых коллекций, и эти представления меняются, если изменяется то, что они «просматривают». С другой стороны, неизменяемые коллекции Guava — это не просто представления структуры исходных данных, а их копии, так что изменения в исходной структуре не влияют на скопированную неизменяемую коллекцию.

 

От http://marxsoftware.blogspot.com/2011/10/immutable-collections-guava-style.html