Location>code7788 >text

【Guava】Collection Tool Class-Immutable&Lists&Maps&Sets

Popularity:344 ℃/2025-03-31 08:19:04

Immutable

like《Effective Java》Item1) As mentioned, when designing classes, it is preferential to use static factory methods rather than constructors to create objects. The advantages are:

  1. The static factory method has an additional layer of name information, which is more expressive than the constructor.
  2. You can create objects more flexibly, such as slow initialization, and cache created objects.
  3. The object type returned inside a static method can be a subclass of its declared type.

Similarly, as《Effective Java》Item17It is stated that variability needs to be minimized,ImmutableListFollow best practices. first,ImmutableListIt cannot be instantiated by constructor, and to be more precise, it cannot be instantiated inpackageExternal instantiation is performed by constructor.

The use of immutable objects in programming can also improve the reliability and maintainability of the code, and its advantages include:

  1. Thread Safety: Immutable objects are thread-safe, no synchronous operations are required, and race conditions are avoided.
  2. Security: It can prevent accidental modifications when the program is running, improving the security of the program
  3. Easy to understand and test: Immutable objects will not change after creation, making them easier to understand and test
  4. Clone and copy: Immutable objects do not need to implement the Clone and Copy logic of mutable objects, because their state is immutable, and cloning is itself

Creating immutable copies of objects is a good defensive programming technique. Guava provides simple and easy-to-use immutable versions for all JDK standard collection types and Guava new collection types. JDK also provides methods to wrap collections into immutable forms.

Problems with JDK immutable collections

JDK's Collections provides an immutable collection of Unmodified Collections, but it only provides a read-only view through the decorator mode. The unmodifiableList itself cannot perform modification operations such as add, but does not prevent modification operations on the original collection. Therefore, what is implemented is not a real immutable collection.

List<String> list=new ArrayList<String>();
 ("a");
 ("b");
 ("c");

 //Create an immutable unmodifiableList collection through list
 List<String> unmodifiableList = (list);
 (unmodifiableList);//[a,b,c]

 //Add elements through list
 ("ddd");
 ("Add an element to list:" + list);//[a,b,c,ddd]
 ("UnmodifiableList after adding elements through list:" + unmodifiableList);[]//[a,b,c,ddd]

  //Add elements through unmodifiableList
 ("ee");//Report an error
 ("Add an element to unmodifiableList:" + unmodifiableList);
  • Clumsy and cumbersome: cannot be used comfortably in all scenarios where you want to make a defensive copy;
  • Insecure: It is necessary to ensure that no one changes through the reference of the original collection, and the returned collection is actually immutable;
  • Inefficient: Packaged collections still retain the overhead of variable collections, such as checks for concurrent modifications, extra space for hash tables, etc.

Guava immutable collection case

The immutable collection provided by Guava is not a view of the original container, but a copy of the original container, so it is simpler and more efficient, ensuring true immutability.

But also note that since immutable only copies the metacontainer itself and is not a deep copy, modifying the referenced content of the original container will also affect immutableXXX

Notice: Each implementation of Guava immutable collection class rejects null values. If you really need a collection class that can accept null values, you can consider it.

Immutable collections can be created in the following ways:

  1. Use the copyOf method, for example, (set)
  2. Use of the of method, such as ("a", "b", "c") or ("a", 1, "b", 2)
  3. Use the Builder class: Reduce the creation of intermediate objects and improve memory usage efficiency.
List<String> list = new ArrayList<>();
("a");
("b");
("c");
("list:" + list);//[a, b, c]

ImmutableList<String> imlist = (list);
("imlist:" + imlist);//[a, b, c]

ImmutableList<String> imOflist = ("seven", "seven1", "seven2");
("imOflist:" + imOflist);//[seven, seven1, seven2]

ImmutableSortedSet<String> imSortList = ("a", "b", "c", "a", "d", "b");
("imSortList:" + imSortList);//[a, b, c, d]

("seven");
("list add a item after list:" + list);//[a, b, c, seven]
("list add a item after imlist:" + imlist);//[a, b, c]

ImmutableSet<Color> imColorSet =
       ImmutableSet.<Color>builder()
             .add(new Color(0, 255, 255))
             .add(new Color(0, 191, 255))
             .build();

("imColorSet:" + imColorSet); //[[r=0,g=255,b=255], [r=0,g=191,b=255]]

Smarter copyOf

Copying elements will be avoided under appropriate circumstances.

ImmutableSet<String> imSet = ("seven", "lisa", "seven1", "lisa1");
("imSet:" + imSet);//[seven, lisa, seven1, lisa1]
ImmutableList<String> imlist = (imSet);
("imlist:" + imlist);//[seven, lisa, seven1, lisa1]
ImmutableSortedSet<String> imSortSet = (imSet);
("imSortSet:" + imSortSet);//[lisa, lisa1, seven, seven1]

List<String> list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
       (i + "x");
 }
("list:" + list);//[0x, 1x, 2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x, 18x, 19x]
ImmutableList<String> imInfolist = ((2, 18));
("imInfolist:" + imInfolist);//[2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x]
int imInfolistSize = ();
("imInfolistSize:" + imInfolistSize);//16
ImmutableSet<String> imInfoSet = ((2, imInfolistSize - 3));
("imInfoSet:" + imInfoSet);//[4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x]

In this code, (imSet) will intelligently return (), which is a List view of constant time complexity of ImmutableSet.

In fact, to achievecopyOfThe easiest way is to directly copy each element of the underlying layer and then generate it.ImmutableList. However, if all cases are copied deeply, the performance and storage overhead will inevitably be relatively large. So how is the source code optimized?

All immutable collections have an asList() method that provides an ImmutableList view, allowing us to easily read collection elements in list form. For example, we can use().get(k) to read the kth minimum element from the ImmutableSortedSet.
The ImmutableList returned by asList() is usually (but not always) overhead stable view implementation, rather than simply copying elements into a List, that is, the list view returned by asList is usually better than the average list, for example, when supported by the underlying collection, it always uses an efficient contain method.

The source code is as follows:

// #copyOf(<? extends E>)
 public static <E> ImmutableList<E> copyOf(Collection<? extends E> elements) {
     //Judge whether it is an immutable set
     if (elements instanceof ImmutableCollection) {
         //If the incoming combination itself is an immutable collection, then asList gets the view and returns it; in fact, it is to directly reuse the original collection
         ImmutableList<E> list = ((ImmutableCollection)elements).asList();
         //Judge whether you want to return to the local view: if yes, rebuild -> call for deep copy; if not, reuse the original
         return () ? asImmutableList(()) : list;
     } else {//If not, execute the construct method: the underlying call is used as a deep copy
         return construct(());
     }
 }

 // #asList
 public ImmutableList<E> asList() {
     switch (()) {
         case 0:
             // Return an empty immutable set, which is a static final constant and can be reused
             return ();
         case 1:
             // Return an immutable collection of SingletonImmutableList
             return (().next());
         default:
             return new RegularImmutableAsList(this, ());
     }
 }

 //#RegularImmutableAsList(<E>, [])
 RegularImmutableAsList(ImmutableCollection<E> delegate, Object[] array) {
     this(delegate, (array));
 }
 RegularImmutableAsList(ImmutableCollection<E> delegate, ImmutableList<? extends E> delegateList) {
      = delegate;
      = delegateList;
 }

In fact, (ImmutableCollection) will try to avoid linear time copying for the following situations:

  • It is possible to use the underlying data structure in constant time: Because it will get the view and return it
  • No memory leak: For example, there is a large immutable setImmutableList<String> hugeList((0, 10))It will be copied explicitly (as in the above source code, it will determine whether it is a partial view) to avoid unnecessarily holding references to hugeList.
  • Don't change the semantics: So (myImmutableSortedSet) will be explicitly copied, because compared with the comparator-based ImmutableSortedSet, ImmutableSet has different semantics for hashCode() and equals.

Avoiding linear copy where possible minimizes the performance overhead of defensive programming styles.

Guava collection and immutable correspondence

Variable collection type Variable collection source: JDK or Guava? Guava immutable collection
Collection JDK ImmutableCollection
List JDK ImmutableList
Set JDK ImmutableSet
SortedSet/NavigableSet JDK ImmutableSortedSet
Map JDK ImmutableMap
SortedMap JDK ImmutableSortedMap
Multiset Guava ImmutableMultiset
SortedMultiset Guava ImmutableSortedMultiset
Multimap Guava ImmutableMultimap
ListMultimap Guava ImmutableListMultimap
SetMultimap Guava ImmutableSetMultimap
BiMap Guava ImmutableBiMap
ClassToInstanceMap Guava ImmutableClassToInstanceMap
Table Guava ImmutableTable

Lists

private Lists() {
}

A private constructor, you can see that this is a real functional function, and the following is analyzing its functions

Functional Function

First, it is classified according to the ability of each function:

Function method
Create an ArrayList method 1、newArrayList()
2、newArrayList(E... elements)
3、newArrayList(Iterable<? extends E> elements)
4、newArrayList(Iterator<? extends E> elements)
5、newArrayListWithCapacity(int initialArraySize)
6、newArrayListWithExpectedSize(int estimatedSize)
Create a LinkedList method 1、newLinkedList()
2、newLinkedList(Iterable<? extends E> elements)
Create a CopyOnWriteArrayList method 1、newCopyOnWriteArrayList()
2、newCopyOnWriteArrayList(Iterable<? extends E> elements)
Create homemade list rules 1、asList(@Nullable E first, E[] rest)
2、asList(@Nullable E first, @Nullable E second, E[] rest)
List Cartesian multiplication product 1、cartesianProduct(List<? extends List<? extends B>> lists)
2、cartesianProduct(List<? extends B>... lists)
List deformation transform(List<F> fromList, Function<? super F, ? extends T> function)
Split list (one of the functions: pagination) partition(List<T> list, int size)
Operate strings as character arrays 1、charactersOf(String string)
2、charactersOf(CharSequence sequence)
Reverse list reverse(List<T> list)

Create an ArrayList method

  1. Create an ArrayList without parameters

    public static <E> ArrayList<E> newArrayList() {
         return new ArrayList();//Return a new ArrayList container directly
     }
  2. Pass in an array and return an ArrayList

    public static <E> ArrayList<E> newArrayList(E... elements) {
         //Realize the array to empty
         (elements);
         // computeArrayListCapacity optimizes the current number
         int capacity = computeArrayListCapacity();
         //Create a new ArrayList based on the optimized number here
         ArrayList list = new ArrayList(capacity);
         //Storage all elements in the array in List
         (list, elements);
         return list;
     }
    
     //The purpose is to calculate the capacity that should be allocated when list initialization in order to avoid or reduce the overhead caused by dynamic expansion to avoid or reduce the amount of overhead caused by dynamic expansion.
     @VisibleForTesting
     static int computeArrayListCapacity(int arraySize) {
         //Make sure arraySize is non-negative
         (arraySize, "arraySize");
         // Make sure the last value is within the range of int
         return (5L + (long)arraySize + (long)(arraySize / 10));
     }
  3. Pass in a collection top-level interface and return an ArrayList

    public static <E> ArrayList<E> newArrayList(Iterable<? extends E> elements) {
         //Just empty the set
         (elements);
         //Disactions are performed according to the actual type of incoming
         return elements instanceof Collection ? new ArrayList((elements)):newArrayList((Iterator)elements.
     iterator());
     }
  4. Pass in an iterator and return an ArrayList

    public static <E> ArrayList<E> newArrayList(Iterator<? extends E> elements) {
         ArrayList list = newArrayList();
         //Load the source code in the iterator into the list collection according to the addAll method in Iterators
         (list, elements);
         return list;
     }
  5. Pass in the desired list length, returning an ArrayList of the same length as the incoming value

    //Return a new ArrayList directly, and the length is the length passed in
     public static <E> ArrayList<E> newArrayListWithCapacity(int initialArraySize) {
         (initialArraySize, "initialArraySize");
         return new ArrayList(initialArraySize);
     }
  6. Pass in a desired list length, return an ArrayList of the length tuned by the program

    public static <E> ArrayList<E> newArrayListWithExpectedSize(int estimatedSize) {
         return new ArrayList(computeArrayListCapacity(estimatedSize));//Returns an ArrayList after length adjustment
     }

Create a LinkedList method

  1. No parameters are passed, but a LinkedList is returned directly.

    public static <E> LinkedList<E> newLinkedList() {
         return new LinkedList();//Return directly a LinkedList
     }
  2. Pass in a container and return a LinkedList

    public static <E> LinkedList<E> newLinkedList(Iterable<? extends E> elements) {
         LinkedList list = newLinkedList();
         //Transfer data to the incoming container using Iterables' addAll method
         (list, elements);
         return list;
     }

Create a CopyOnWriteArrayList method

  1. No parameters are passed, and a new CopyOnWriteArrayList is returned directly.

    public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() {
         return new CopyOnWriteArrayList();//Return a new CopyOnWriteArrayList directly
     }
  2. Pass in a container, returning a CopyOnWriteArrayList with the value passed in the container

    public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(Iterable<? extends E> elements) {
        Object elementsCollection = elements instanceof Collection?(elements):newArrayList((Iterable)elements);
        return new CopyOnWriteArrayList((Collection)elementsCollection);
    }
    

Create homemade list rules

Use cases:

String leader = "leader";
String[] members = {"member1", "member2", "member3"};

List<String> group = (leader, members);
(group);

One benefit of doing this is that it can improve the readability of the code, as it clearly distinguishes "leader" and "members" rather than mixing them together. And if "members" are dynamically determined (e.g., they come from another method or calculation result), then thisasListMethods will be created more than manualListAnd add elements more convenient.

Notice: asList returns a view, that is, changes to the original container will affect the container content returned by these methods

  1. Generate a List of more than one parameter based on the parameters

    public static <E> List<E> asList(@Nullable E first, E[] rest) {
         return new (first, rest);//Return the internal class OnePlusArrayList in Lists
     }
    
     private static class OnePlusArrayList<E> extends AbstractList<E> implements Serializable, RandomAccess {
         final E first;
         final E[] rest;
         private static final long serialVersionUID = 0L;
     
         OnePlusArrayList(@Nullable E first, E[] rest) {
              = first;
              = (Object[])(rest);
         }
     
         // Rewrite the size and get methods
         // Because it is 1 more number than the original array, the size method performs +1 operation on the original basis.
         public int size() {
             return + 1;
         }
     
         //Rewriting of get is to logically move all subscripts back one by one.
         public E get(int index) {
             (index, ());
             return index == 0 ? :[index - 1];
         }
     }
  2. Generate a List with more than two parameters based on the parameters

    public static <E> List<E> asList(@Nullable E first, @Nullable E second, E[] rest) {
         return new (first, second, rest);//Return an internal class TwoPlusArrayList in Lists
     }
    
     private static class TwoPlusArrayList<E> extends AbstractList<E> implements Serializable, RandomAccess {
         final E first;
         final E second;
         final E[] rest;
         private static final long serialVersionUID = 0L;
     
         TwoPlusArrayList(@Nullable E first, @Nullable E second, E[] rest) {
              = first;
              = second;
              = (Object[])(rest);
         }
     
         // Rewrite the two methods size and get, and size operates +2 on the actual array length
         public int size() {
             return + 2;
         }
     
         //get logically shifts the subscript backward by two bits
         public E get(int index) {
             switch(index) {
             case 0:
                 return ;
             case 1:
                 return ;
             default:
                 (index, ());
                 return [index - 2];
             }
         }
     }

List Cartesian multiplication product

public static <B> List<List<B>> cartesianProduct(List<? extends List<? extends B>> lists) {
     return (lists);
 }

 //The logic of the whole method is to convert a set of input lists into a list structure representing all their possible combinations (Cartesian products), while ensuring immutability of the input list.
 //This method is particularly useful when it is necessary to deal with multidimensional data combinations, such as in application scenarios such as configuration management and test case generation.
 static <E> List<List<E>> create(List<? extends List<? extends E>> lists) {
     <List<E>> axesBuilder = new (());
     Iterator var2 = ();

     while(()) {
         List<? extends E> list = (List)();
         List<E> copy = (list);
         if (()) {
             //If any sublist is empty, the entire Cartesian product list should also be empty (because anything is an empty set with the Cartesian product of the empty set), thus directly returning an empty immutable list.
             return ();
         }

         (copy);
     }

     return new CartesianList(());
 }

List deformation

Use cases:

List<String> stringList = ("1", "2", "3");
List<Integer> integerList = transform(stringList, s -> (s));

Source code:

public static <F, T> List<T> transform(List<F> fromList, Function<? super F, ? extends T> function) {
     //Decide which internal implementation method is used to improve efficiency based on whether fromList supports random access (such as ArrayList).
     //However, no matter which way to handle it, the listIterator method is rewritten in their implementation class
     return (List)(fromList instance of RandomAccess?new (fromList, function):new (fromList, function));
 }


 private static class TransformingRandomAccessList<F, T> extends AbstractList<T> implements RandomAccess, Serializable {
     final List<F> fromList;
     final Function<? super F, ? extends T> function;
     private static final long serialVersionUID = 0L;
 
     TransformingRandomAccessList(List<F> fromList, Function<? super F, ? extends T> function) {
          = (List)(fromList);
          = (Function)(function);
     }
     public ListIterator<T> listIterator(int index) {
         //When getting the iterator through the listIterator method, it is actually the iterator of the original list fromList and wrapping it in a TransformedListIterator.
		 //Each iteration, the TransformedListIterator calls the transform method, which uses function to convert elements from the original list to the target type T.
         return new TransformedListIterator((index)) {
             T transform(F from) {
                 return (from);
             }
         };
     }
 }

Split list (one of the functions: pagination)

// The program classifies and processes according to the incoming List
 public static <T> List<List<T>> partition(List<T> list, int size) {
     (list);
     (size > 0);
     return (List)(list instanceof RandomAccess?new (list, size):new (list, size));
 }

The RandomAccessPartition here is a subclass of Partition, and the processing of RandomAccessPartition directly calls the parent class method, so we only need to parse the Partition class.

private static class Partition<T> extends AbstractList<List<T>> {
     final List<T> list;
     final int size;
     Partition(List<T> list, int size) {
          = list;
          = size;
     }
     //The get method performs intercepting operation, and its interception is implemented by subList.  What subscript of the list it gets is
     public List<T> get(int index) {
         (index, ());
         int start = index * ;
         int end = (start + , ());
         return (start, end);
     }
     public int size() {
         return ((), , );
     }
     public boolean isEmpty() {
         return ();
     }
 }

Operate strings as character arrays

Mainly used to convert a string into an immutableList<Character>, so that the characters of the string can be operated like a list element.

//The two methods receive strings and CharSequence as parameters respectively, and pass parameters into the CharSequenceAsList class for processing:
 public static ImmutableList<Character> charactersOf(String string) {
     return new ((String)(string));
 }
 public static List<Character> charactersOf(CharSequence sequence) {
     return new ((CharSequence)(sequence));
 }
//In fact, it is to process strings indirectly using methods in String class
 private static final class StringAsImmutableList extends ImmutableList<Character> {
     private final String string;
 
     StringAsImmutableList(String string) {
          = string;
     }
 
     public int indexOf(@Nullable Object object) {
         return object instanceof Character?((((Character)object).charValue()):-1;
     }
 
     public int lastIndexOf(@Nullable Object object) {
         return object instanceof Character?((((Character)object).charValue()):-1;
     }
 
     public ImmutableList<Character> subList(int fromIndex, int toIndex) {
         (fromIndex, toIndex, ());
         return ((String)(fromIndex, toIndex));
     }
 
     boolean isPartialView() {
         return false;
     }
 
     public Character get(int index) {
         (index, ());
         return ((index));
     }
 
     public int size() {
         return ();
     }
 }

list reverse order

public static <T> List<T> reverse(List<T> list) {
    if (list instanceof ImmutableList) {
        List<?> reversed = ((ImmutableList)list).reverse();
        List<T> result = reversed;
        return result;
    } else if (list instanceof ReverseList) {
        return ((ReverseList)list).getForwardList();
    } else {
        return (List)(list instanceof RandomAccess ? new RandomAccessReverseList(list) : new ReverseList(list));
    }
}

In fact, the reverse method of the ImmutableList class is called for the reverse order of processing

Maps

private Maps() {
}

A private constructor, you can see that this is a real functional function, and the following is analyzing its functions

Functional Function

Function method
Create an EnumMap 1、EnumMap<K, V> newEnumMap(Class<K> type)
2、EnumMap<K, V> newEnumMap(Map<K, ? extends V> map)
Returns immutable EnumMap ImmutableMap<K, V> immutableEnumMap(Map<K, ? extends V> map)
Create a HashMap 1、HashMap<K, V> newHashMap()
2、HashMap<K, V> newHashMapWithExpectedSize(int expectedSize)
3、HashMap<K, V> newHashMap(Map<? extends K, ? extends V> map)
Create LinkedHashMap 1、LinkedHashMap<K, V> newLinkedHashMap()
2、LinkedHashMap<K, V> newLinkedHashMap(Map<? extends K, ? extends V> map)
Create a ConcurrentMap ConcurrentMap<K, V> newConcurrentMap()
Create TreeMap 1、TreeMap<K, V> newTreeMap()
2、TreeMap<K, V> newTreeMap(SortedMap<K, ? extends V> map)
3、TreeMap<K, V> newTreeMap(@Nullable Comparator<C> comparator)
Create IdentityHashMap IdentityHashMap<K, V> newIdentityHashMap()
Get the values ​​of different elements in two maps 1、MapDifference<K, V> difference(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right)
2、MapDifference<K, V> difference(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right, Equivalence<? super V> valueEquivalence)
3、SortedMapDifference<K, V> difference(SortedMap<K, ? extends V> left, Map<? extends K, ? extends V> right)
Construct Map based on functions and set 1、Map<K, V> asMap(Set<K> set, Function<? super K, V> function)
2、SortedMap<K, V> asMap(SortedSet<K> set, Function<? super K, V> function)
3、NavigableMap<K, V> asMap(NavigableSet<K> set, Function<? super K, V> function)
Construct immutable Map based on functions and iterators 1、ImmutableMap<K, V> toMap(Iterator<K> keys, Function<? super K, V> valueFunction)
2、ImmutableMap<K, V> toMap(Iterator<K> keys, Function<? super K, V> valueFunction)
3、ImmutableMap<K, V> uniqueIndex(Iterable<V> values, Function<? super V, K> keyFunction)
4、ImmutableMap<K, V> uniqueIndex(Iterator<V> values, Function<? super V, K> keyFunction)
Read data from configuration files and create immutable maps ImmutableMap<String, String> fromProperties(Properties properties)
Returns Entry or Entry collection 1、Entry<K, V> immutableEntry(@Nullable K key, @Nullable V value)
2、Set<Entry<K, V>> unmodifiableEntrySet(Set<Entry<K, V>> entrySet)
3、Entry<K, V> unmodifiableEntry(final Entry<? extends K, ? extends V> entry)
Returns a special BiMap class 1、BiMap<K, V> synchronizedBiMap(BiMap<K, V> bimap)
2、BiMap<K, V> unmodifiableBiMap(BiMap<? extends K, ? extends V> bimap)
Transforming Map based on Map and Function 1、Map<K, V2> transformValues(Map<K, V1> fromMap, Function<? super V1, V2> function)
2、SortedMap<K, V2> transformValues(SortedMap<K, V1> fromMap, Function<? super V1, V2> function)
3、NavigableMap<K, V2> transformValues(NavigableMap<K, V1> fromMap, Function<? super V1, V2> function)
4、Map<K, V2> transformEntries(Map<K, V1> fromMap, <? super K, ? super V1, V2> transformer)
5、SortedMap<K, V2> transformEntries(SortedMap<K, V1> fromMap, <? super K, ? super V1, V2> transformer)
6、NavigableMap<K, V2> transformEntries(NavigableMap<K, V1> fromMap, <? super K, ? super V1, V2> transformer)
Use functions to filter the map and return the same type of map It is divided into 4 types of filters
1. Filter for Keys
<K, V> filterKeys(Map<K, V> unfiltered, Predicate<? super K> keyPredicate)
<K, V> filterKeys(SortedMap<K, V> unfiltered, Predicate<? super K> keyPredicate)
<K, V> filterKeys(NavigableMap<K, V> unfiltered, Predicate<? super K> keyPredicate)
<K, V> filterKeys(BiMap<K, V> unfiltered, Predicate<? super K> keyPredicate)
2. Filter for Value
<K, V> filterValues(Map<K, V> unfiltered, Predicate<? super V> valuePredicate)
<K, V> filterValues(SortedMap<K, V> unfiltered, Predicate<? super V> valuePredicate)
<K, V> filterValues(NavigableMap<K, V> unfiltered, Predicate<? super V> valuePredicate)
<K, V> filterValues(BiMap<K, V> unfiltered, Predicate<? super V> valuePredicate)
3. Filter for Entry
<K, V> filterEntries(Map<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate)
<K, V> filterEntries(SortedMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate)
<K, V> filterSortedIgnoreNavigable(SortedMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate)
<K, V> filterEntries(NavigableMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate)
<K, V> filterEntries(BiMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate)
4. Filter for maps containing filtering rules
<K, V> filterFiltered(<K, V> map, Predicate<? super Entry<K, V>> entryPredicate)
<K, V> filterFiltered(<K, V> map, Predicate<? super Entry<K, V>> entryPredicate)
<K, V> filterFiltered(<K, V> map, Predicate<? super Entry<K, V>> entryPredicate)
<K, V> filterFiltered(<K, V> map, Predicate<? super Entry<K, V>> entryPredicate)

Create an EnumMap

  1. Pass in a Class variable and return an EnumMap

    public static <K extends Enum<K>, V> EnumMap<K, V> newEnumMap(Class<K> type) {
        return new EnumMap((Class)(type));
    }
    
  2. Pass in a Map variable and return an EnumMap

    public static <K extends Enum<K>, V> EnumMap<K, V> newEnumMap(Map<K, ? extends V> map) {
         return new EnumMap(map);
     }
    
     //#EnumMap(<K,? extends V>)
     public EnumMap(Map<K, ? extends V> m) {
         if (m instanceof EnumMap) {
             EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
             keyType = ;
             keyUniverse = ;
             vals = ();
             size = ;
         } else {
             if (())
                 throw new IllegalArgumentException("Specified map is empty");
             keyType = ().iterator().next().getDeclaringClass();
             keyUniverse = getKeyUniverse(keyType);
             vals = new Object[];
             //The step of assigning the incoming Map to the new EnumMap is operated by the putAll method of EnumMap.
             putAll(m);
         }
     }

Returns immutable EnumMap

Pass in a map, return an immutable Map container

public static <K extends Enum<K>, V> ImmutableMap<K, V> immutableEnumMap(Map<K, ? extends V> map) {
     if(map instanceof ImmutableEnumMap) {
         //If the map is ImmutableEnumMap type, it will be directly forced to convert to a container of ImmutableEnumMap type
         ImmutableEnumMap<K, V> result = (ImmutableEnumMap)map;
         return result;
     } else {
         Iterator<? extends <K, ? extends V>> entryItr = ().iterator();
         if (!()) {
             //If the map is empty, it will directly return the newly created empty ImmutableMap container
             return ();
         } else {
             <K, ? extends V> entry1 = ()();
             K key1 = (Enum)();
             V value1 = ();
             (key1, value1);
             Class<K> clazz = ();
             EnumMap<K, V> enumMap = new EnumMap(clazz);
             (key1, value1);

             while(()) {
                 <K, ? extends V> entry = ()();
                 K key = (Enum)();
                 V value = ();
                 //Neither key nor value can be null
                 (key, value);
                 (key, value);
             }
			
             //The above is true, then use the function in ImmutableEnumMap to convert it directly to map
             return (enumMap);
         }
     }
 }

Create a HashMap

  1. Return a new HashMap directly

    public static <K, V> HashMap<K, V> newHashMap() {
        return new HashMap();
    }
    
  2. Returns a HashMap with initial length

    public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(int expectedSize) {
         return new HashMap(capacity(expectedSize));
     }
    
     //Return a HashMap with actual expectedSize based on the passed length. The capacity method is a method to calculate the actual length.
     static int capacity(int expectedSize) {
         if(expectedSize < 3) {
             (expectedSize, "expectedSize");
             return expectedSize + 1;
         } else {
             // Suppose that the incoming is 8, and the returned is 8/0.75 + 1 = 11. According to the principle of HashMap, the capacity will be expanded to 16.  So what are you considering here?  Why is this set
             return expectedSize < 1073741824 ? (int)((float)expectedSize / 0.75F + 1.0F) : Integer.MAX_VALUE;
         }
     }

    Why is the coefficient 0.75 here?

  3. Pass in a Map variable and return a HashMap

    public static <K, V> HashMap<K, V> newHashMap(Map<? extends K, ? extends V> map) {
         return new HashMap(map);//This step is operated by HashMap's putMapEntries method
     }

Create LinkedHashMap

  1. Return a new LinkedHashMap directly

    public static <K, V> LinkedHashMap<K, V> newLinkedHashMap() {
        return new LinkedHashMap();
    }
    
  2. Pass in a Map variable and return a LinkedHashMap

    public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(Map<? extends K, ? extends V> map) {
         return new LinkedHashMap(map);//This step is operated by the putMapEntries method of LinkedHashMap.
     }

Create a ConcurrentMap

Return a new ConcurrentMap directly

public static <K, V> ConcurrentMap<K, V> newConcurrentMap() {
    return new ConcurrentHashMap();
}

Create TreeMap

  1. Return a new TreeMap directly

    public static <K extends Comparable, V> TreeMap<K, V> newTreeMap() {
        return new TreeMap();
    }
    
  2. Pass in a Map variable, return a TreeMap, and assign the Map value to TreeMap

    public static <K, V> TreeMap<K, V> newTreeMap(SortedMap<K, ? extends V> map) {
        return new TreeMap(map);
    }
    
  3. Pass in a comparison interface and return a TreeMap formed according to the incoming comparison rules.

    public static <C, K extends C, V> TreeMap<K, V> newTreeMap(@Nullable Comparator<C> comparator) {
        return new TreeMap(comparator);
    }
    

Create IdentityHashMap

Return an identityHashMap directly

public static <K, V> IdentityHashMap<K, V> newIdentityHashMap() {
    return new IdentityHashMap();
}

The difference between IdentityHashMap and HashMap is that it can store the same value of the same class. In fact, whether the addition operation in put uses equals but uses == for judgment.

Get the values ​​of different elements in two maps

Before explaining these three methods in Maps, let’s first understand what the MapDifference interface and implementation class MapDifferenceImpl can represent:

public interface MapDifference<K, V> {
     boolean areEqual();
     Map<K, V> entriesOnlyOnLeft();
     Map<K, V> entriesOnlyOnRight();
     Map<K, V> entriesInCommon();
     Map<K, ValueDifference<V>> entriesDiffering();
     boolean equals(@CheckForNull Object var1);
     int hashCode();

     @DoNotMock("Use ")
     public interface ValueDifference<V> {
         @ParametricNullness
         V leftValue();
         @ParametricNullness
         V rightValue();
        
         boolean equals(@CheckForNull Object var1);
         int hashCode();
     }
 }

 static class MapDifferenceImpl<K, V> implements MapDifference<K, V> {
     final Map<K, V> onlyOnLeft;
     final Map<K, V> onlyOnRight;
     final Map<K, V> onBoth;
     final Map<K, ValueDifference<V>> differences;
 
     MapDifferenceImpl(Map<K, V> onlyOnLeft, Map<K, V> onlyOnRight, Map<K, V> onBoth, Map<K, ValueDifference<V>> differences) {
          = (onlyOnLeft);
          = (onlyOnRight);
          = (onBoth);
          = (differences);
     }
    
     public boolean areEqual() {
         return () && () && ();
     }
     public Map<K, V> entriesOnlyOnLeft() {return ; }
     public Map<K, V> entriesOnlyOnRight() {return ; }
     public Map<K, V> entriesInCommon() {return ; }
     public Map<K, ValueDifference<V>> entriesDiffering() { return ; }
    
     //Equals and other methods...
 }

You can see that there are 4 variables in the MapDifferenceImpl implementation class

  • onlyOnLeft only stores only the unique Map with variable name left;
  • onlyOnRight only stores only the unique Map with variable name right;
  • onBoth stores elements with common keys and equal values ​​in two maps;
  • differences Because the type of value storage is ValueDifference, differences store common keys and different elements.
  1. Pass in two Map variables, and judge which difference method is given to process according to the type of Map of the left variable name.

    public static <K, V> MapDifference<K, V> difference(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right) {
        if(left instanceof SortedMap) {
            SortedMap<K, ? extends V> sortedLeft = (SortedMap)left;
            return difference(sortedLeft, right);
        } else {
            MapDifference<K, V> result = difference(left, right, ());
            return result;
        }
    }
    
  2. Pass in two maps and use the doDifference method to classify the elements in the two maps.

    public static <K, V> MapDifference<K, V> difference(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right, Equivalence<? super V> valueEquivalence) {
         (valueEquivalence);
         HashMap onlyOnLeft = newHashMap();
         HashMap onlyOnRight = new HashMap(right);
         HashMap onBoth = newHashMap();
         Map<K, <V>> differences = newLinkedHashMap();
         doDifference(left, right, valueEquivalence, onlyOnLeft, onlyOnRight, onBoth, differences);
         return new (onlyOnLeft, onlyOnRight, onBoth, differences);
     }
    
     private static <K, V> void doDifference(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right, Equivalence<? super V> valueEquivalence, Map<K, V> onlyOnLeft, Map<K, V> onlyOnRight, Map<K, V> onBoth, Map<K, <V>> differences) {
         Iterator var7 = ().iterator();
    
         //Travel left
         while(()) {
             <? extends K, ? extends V> entry = ()();
             K leftKey = ();
             V leftValue = ();
             // Check whether the key value is included in the right
             if ((leftKey)) {
                 //If this key value is also included in the right, remove this value in the right
                 V rightValue = ((leftKey));
                 //Judge whether the left and right value of this key value are equal
                 if ((leftValue, rightValue)) {
                     //If the two values ​​are equal, fill them into the onBoth Map container
                     (leftKey, leftValue);
                 } else {
                     //If the two values ​​are not equal, fill them into the differences Map container
                     (leftKey, (leftValue, rightValue));
                 }
             } else {
                 //If this key does not exist in right, fill it in onlyOnLeft container
                 (leftKey, leftValue);
             }
         }
     }
  3. Pass in a SortedMap and a Map variable, and return a classed class

    public static <K, V> SortedMapDifference<K, V> difference(SortedMap<K, ? extends V> left, Map<? extends K, ? extends V> right) {
        (left);
        (right);
        Comparator<? super K> comparator = orNaturalOrder(());
        SortedMap<K, V> onlyOnLeft = newTreeMap(comparator);
        SortedMap<K, V> onlyOnRight = newTreeMap(comparator);
        (right);
        SortedMap<K, V> onBoth = newTreeMap(comparator);
        SortedMap<K, <V>> differences = newTreeMap(comparator);
        doDifference(left, right, (), onlyOnLeft, onlyOnRight, onBoth, differences);
        return new SortedMapDifferenceImpl(onlyOnLeft, onlyOnRight, onBoth, differences);
    }
    

Construct Map based on functions and set

This method shows how to transfer aSetAnd oneFunctionTogether, create a view that dynamically generates key-value pairs by applying functions every time you query.

  1. View FeaturesAsMapViewWhat is created is a view, not a separate new collection. This means the original collection (SetAny modifications to ) will be reflected inAsMapViewand vice versa. This behavior is similar to how()The method obtains an unmodified view, butAsMapViewis modifiable, and its modification will affect the original collection.
  2. Delay loadingAsMapViewIn actual callget()The value corresponding to the key is not calculated before the method. This means that if you have a very expensive conversion logic, it will only be executed if the value is actually needed, which helps to improve efficiency.
  3. use: This class is very suitable for creating dynamically computed maps where the values ​​of the map are key-dependent and may not wish to calculate all possible values ​​in advance. For example, it can be used to generate configuration settings, perform data conversion, etc. as needed.
  4. accomplish: Inside,AsMapViewUse the providedSetandFunctionTo achieveMapinterface. When calledget(Object key)When, ifkeyExist in a collection, useFunctionto calculate the value.

Use cases:

//Suppose there is a set of product IDs, and the product price needs to be dynamically obtained based on the product ID.  Price calculation may depend on a variety of factors, such as real-time supply and demand status of goods, promotional activities, etc., which can be implemented through one function:
 Set<Integer> productIds = new HashSet<>();
 (1);
 (2);

 Function<Integer, Double> priceFunction = productId -> {
     // Suppose some complex calculations are performed here to determine the price
     return productId * 10.0; // Simple example
 };

 Map<Integer, Double> priceMap = (productIds, priceFunction);

 ("Price of Product 1: " + (1)); // Output 10.0
 ("Price of Product 2: " + (2)); // Output 20.0

Method introduction:

  1. Pass in a set and a rule, return a map

    public static <K, V> Map<K, V> asMap(Set<K> set, Function<? super K, V> function) {
        return new AsMapView(set, function);
    }
    
  2. Pass in a SortedSet and a rule, and return a SortMap

    public static <K, V> SortedMap<K, V> asMap(SortedSet<K> set, Function<? super K, V> function) {
        return new SortedAsMapView(set, function);
    }
    
  3. Pass in a NavigableSet and a rule, return a NavigableMap

    public static <K, V> NavigableMap<K, V> asMap(NavigableSet<K> set, Function<? super K, V> function) {
        return new (set, function);
    }
    

Construct immutable Map based on functions and iterators

This method passes in an iterator of the container and a rule, and then returns an immutable Map container

  1. Pass in a key value container and a rule, and hand it over to the overloaded function for processing, returning an immutable Map container

    public static <K, V> ImmutableMap<K, V> toMap(Iterable<K> keys, Function<? super K, V> valueFunction) {
        return toMap((Iterator)(), valueFunction);
    }
    
    
  2. Pass in a key value iterator and a rule to return an immutable map container

    public static <K, V> ImmutableMap<K, V> toMap(Iterator<K> keys, Function<? super K, V> valueFunction) {
         (valueFunction);
         LinkedHashMap builder = newLinkedHashMap();
         //Use the value in the iterator as the key value, use the value calculated by the rule as the value value, and store it in the builder
         while(()) {
             Object key = ();
             (key, (key));
         }
         //Return an immutable container
         return (builder);
     }
  3. According to the value value container and a rule, it is directly handed over to the overload function for processing, and returns an immutable Map container

    public static <K, V> ImmutableMap<K, V> uniqueIndex(Iterable<V> values, Function<? super V, K> keyFunction) {
        return uniqueIndex((Iterator)(), keyFunction);
    }
    
  4. Passing in a value iterator and a rule to return an immutable map container

    public static <K, V> ImmutableMap<K, V> uniqueIndex(Iterator<V> values, Function<? super V, K> keyFunction) {
         (keyFunction);
         Builder builder = ();
         //Use the value in the iterator as the value value, use the value calculated by the rule as the key value, and store it in the builder
         while(()) {
             Object value = ();
             ((value), value);
         }
         //Return an immutable container
         return ();
     }

Read data from properties file and create immutable map

Key and value obtained from Properties, return an immutable map

public static ImmutableMap<String, String> fromProperties(Properties properties) {
     Builder builder = ();
     Enumeration e = ();
     //The key and value obtained from properties are assigned to the builder
     while(()) {
         String key = (String)();
         (key, (key));
     }
     //Return an immutable map
     return ();
 }

Returns Entry or Entry collection

Pass in a key and a value, return an immutable Entry

public static <K, V> Entry<K, V> immutableEntry(@Nullable K key, @Nullable V value) {
    return new ImmutableEntry(key, value);
}

Returns a special BiMap class

Guava provides BiMap support to support bidirectional mapping relationships. For details about BiMap, you can read the following article.

  1. Passing in a BiMap returns a thread-safe BiMap

    public static <K, V> BiMap<K, V> synchronizedBiMap(BiMap<K, V> bimap) {
        return (bimap, (Object)null);
    }
    
  2. Passing in a BiMap returns an unmodifiableBiMap

    public static <K, V> BiMap<K, V> unmodifiableBiMap(BiMap<? extends K, ? extends V> bimap) {
        return new (bimap, (BiMap)null);
    }
    

Transforming Map based on Map and Function

This method uses functional programming, using a Map value as the key of the new Map, and calculates the value of the new Map according to the function rules. This conversion will only be calculated when viewed, and the real stored map is the passed in

  1. Pass in a map and a rule, and return a map calculated with rules

    public static <K, V1, V2> Map<K, V2> transformValues(Map<K, V1> fromMap, Function<? super V1, V2> function) {
        return transformEntries((Map)fromMap, asEntryTransformer(function));
    }
    
  2. Pass in a SortedMap and a rule, and return a new map calculated by the rule.

    public static <K, V1, V2> SortedMap<K, V2> transformValues(SortedMap<K, V1> fromMap, Function<? super V1, V2> function) {
        return transformEntries((SortedMap)fromMap, asEntryTransformer(function));
    }
    
  3. Pass in a NavigableMap and a rule, and return a NavigableMap calculated by the rule

    public static <K, V1, V2> NavigableMap<K, V2> transformValues(NavigableMap<K, V1> fromMap, Function<? super V1, V2> function) {
        return transformEntries((NavigableMap)fromMap, asEntryTransformer(function));
    }
    
  4. Pass in a Map and a Maps rule format, and return a new Map according to the rules.

    public static <K, V1, V2> Map<K, V2> transformEntries(Map<K, V1> fromMap, <? super K, ? super V1, V2> transformer) {
        return (Map)(fromMap instanceof SortedMap?transformEntries((SortedMap)((SortedMap)fromMap), transformer):new (fromMap, transformer));
    }
    
  5. Pass in a NavigableMap and a Maps rule format, and return a new NavigableMap according to the rules.

    public static <K, V1, V2> NavigableMap<K, V2> transformEntries(NavigableMap<K, V1> fromMap, <? super K, ? super V1, V2> transformer) {
        return new (fromMap, transformer);
    }
    

Use functions to filter the map and return the same type of map

Here we mainly analyze the source code filtered by Key. Of course, Maps also provides some methods to filter Value, Entry, and Map containing filters. Like the above method of filtering keys, they inherit the AbstractFilteredMap abstract class and implement their respective filtering functions.

Use the key Predicate function interface to specify the filtering rules and filter the Map. The FilteredKeyMap source code for the Key in the Map is as follows:

// The KeySet was filtered and the filter method in the Set was used
 private static class FilteredKeyMap<K, V> extends <K, V> {
     Predicate<? super K> keyPredicate;
     FilteredKeyMap(Map<K, V> unfiltered, Predicate<? super K> keyPredicate, Predicate<? super Entry<K, V>> entryPredicate) {
         super(unfiltered, entryPredicate);
          = keyPredicate;
     }
     protected Set<Entry<K, V>> createEntrySet() {
         return ((), );
     }
 
     Set<K> createKeySet() {
         return ((), );
     }
 
     public boolean containsKey(Object key) {
         return (key) && (key);
     }
 }
  1. Pass in a map and filter its rules and return a new map

    public static <K, V> Map<K, V> filterKeys(Map<K, V> unfiltered, Predicate<? super K> keyPredicate) {
            (keyPredicate);
            Predicate<<K, ?>> entryPredicate = keyPredicateOnEntries(keyPredicate);
            return (Map)(unfiltered instanceof AbstractFilteredMap ? filterFiltered((AbstractFilteredMap)unfiltered, entryPredicate) : new FilteredKeyMap((Map)(unfiltered), keyPredicate, entryPredicate));
        }
    
  2. Pass in a SortedMap and hand it over to the filterEntries method for processing

    public static <K, V> SortedMap<K, V> filterKeys(SortedMap<K, V> unfiltered, Predicate<? super K> keyPredicate) {
        return filterEntries(unfiltered, keyPredicateOnEntries(keyPredicate));
    }
    
  3. Pass in a NavigableMap and hand it over to the filterEntries method for processing

    public static <K, V> NavigableMap<K, V> filterKeys(NavigableMap<K, V> unfiltered, Predicate<? super K> keyPredicate) {
        return filterEntries((NavigableMap)unfiltered, keyPredicateOnEntries(keyPredicate));
    }
    
  4. Pass in a BiMap and hand it over to the filterEntries method for processing

    public static <K, V> BiMap<K, V> filterKeys(BiMap<K, V> unfiltered, Predicate<? super K> keyPredicate) {
        (keyPredicate);
        return filterEntries((BiMap)unfiltered, keyPredicateOnEntries(keyPredicate));
    }
    

Sets

Functional Function

Function method
Create immutable set 1、ImmutableSet<E> immutableEnumSet(E anElement, E... otherElements)
2、ImmutableSet<E> immutableEnumSet(Iterable<E> elements)
Create a HashSet 1、HashSet<E> newHashSet()
2、HashSet<E> newHashSet(E... elements)
3、HashSet<E> newHashSetWithExpectedSize(int expectedSize)
4、HashSet<E> newHashSet(Iterable<? extends E> elements)
5、HashSet<E> newHashSet(Iterator<? extends E> elements)
Create a thread-safe Set 1、Set<E> newConcurrentHashSet()
2、Set<E> newConcurrentHashSet(Iterable<? extends E> elements)
Create LinkedHashMap 1、LinkedHashSet<E> newLinkedHashSet()
2、LinkedHashSet<E> newLinkedHashSetWithExpectedSize(int expectedSize)
3、LinkedHashSet<E> newLinkedHashSet(Iterable<? extends E> elements)
Create TreeSet 1、TreeSet<E> newTreeSet()
2、TreeSet<E> newTreeSet(Iterable<? extends E> elements)
3、TreeSet<E> newTreeSet(Comparator<? super E> comparator)
Create IdentityHashSet Set<E> newIdentityHashSet()
Create a CopyOnWriteArraySet 1、CopyOnWriteArraySet<E> newCopyOnWriteArraySet()
2、CopyOnWriteArraySet<E> newCopyOnWriteArraySet(Iterable<? extends E> elements)
Create an EnumSet 1、EnumSet<E> newEnumSet(Iterable<E> iterable, Class<E> elementType)
2、EnumSet<E> complementOf(Collection<E> collection)
3、EnumSet<E> complementOf(Collection<E> collection, Class<E> type)
4、EnumSet<E> makeComplementByHand(Collection<E> collection, Class<E> type)
Create a Set from Map Set<E> newSetFromMap(Map<E, Boolean> map)
Take the union of two sets as view <E> union(final Set<? extends E> set1, final Set<? extends E> set2)
Take the intersection of two sets as view <E> intersection(final Set<E> set1, final Set<?> set2)
Take the non-overlapping part of the two sets as the view <E> difference(final Set<E> set1, final Set<?> set2)
Take the symmetrical parts of two Sets as the view <E> symmetricDifference(Set<? extends E> set1, Set<? extends E> set2)
Filter Set 1、filter(Set<E> unfiltered, Predicate<? super E> predicate)
2、SortedSet<E> filter(SortedSet<E> unfiltered, Predicate<? super E> predicate)
3、SortedSet<E> filterSortedIgnoreNavigable(SortedSet<E> unfiltered, Predicate<? super E> predicate)
4、NavigableSet<E> filter(NavigableSet<E> unfiltered, Predicate<? super E> predicate)
Get the Cartesian product of two sets 1、Set<List<B>> cartesianProduct(List<? extends Set<? extends B>> sets)
2、Set<List<B>> cartesianProduct(Set<? extends B>... sets)

Create an immutable set

  1. Create an immutable Set based on the passed parameters

    public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet(E anElement, E... otherElements) {
         //Use function to convert EnumSet to ImmutableSet
         return ((anElement, otherElements));
     }
    
     //The method is to merge anElement and otherElements into an EnumSet
     public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
         EnumSet<E> result = noneOf(());
         //Insert the first parameter into EnumSet first
         (first);
         //Insert all the passed array into EnumSet
         for (E e : rest)
             (e);
         return result;
     }
  2. Create an immutable Set from a collection

    public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet(Iterable<E> elements) {
         //If it is an ImmutableEnumSet, it will be converted directly to ImmutableEnumSet
         if(elements instanceof ImmutableEnumSet) {
             return (ImmutableEnumSet) elements;
         } else if(elements instanceof Collection) {
             //If it is a Collection and is not empty, use the method directly to convert it into ImmutableEnumSet
             Collection itr1 = (Collection)elements;
             return ()?():((itr1));
         } else {
             //Other types, get its iterator and make an ImmutableEnumSet
             Iterator itr = ();
             if(()) {
                 EnumSet enumSet = ((Enum)());
                 (enumSet, itr);
                 return (enumSet);
             } else {
                 return ();
             }
         }
     }

Create a HashSet

  1. Directly new a HashSet

    public static <E> HashSet<E> newHashSet() {
        return new HashSet();
    }
    
  2. Pass in an array and return a HashSet

    public static <E> HashSet<E> newHashSet(E... elements) {
         //Create a HashSet with the expected size
         HashSet set = newHashSetWithExpectedSize();
         // Assign all elements in the array to the new HashSet
         (set, elements);
         return set;
     }
  3. Create a HashSet of the expected size

    public static <E> HashSet<E> newHashSetWithExpectedSize(int expectedSize) {
         //Create a HashSet with the size of the value calculated by the method. This method finally returns 4/3 of the original value
         return new HashSet((expectedSize));
     }
  4. Create a HashSet from the incoming collection

    public static <E> HashSet<E> newHashSet(Iterable<? extends E> elements) {
        return elements instanceof Collection?new HashSet((elements)):newHashSet((Iterator)());
    }
    
  5. Create a HashSet based on the incoming iterator

    public static <E> HashSet<E> newHashSet(Iterator<? extends E> elements) {
         //Create a HashSet
         HashSet set = newHashSet();
         //Add elements from the iterator to set using the method in Guava
         (set, elements);
         return set;
     }

Create a thread-safe Set

  1. Create a Set using ConcurrentHashMap

    public static <E> Set<E> newConcurrentHashSet() {
         //Create a ConcurrentHashMap and use the newSetFromMap method to convert the value of ConcurrentHashMap to Set
         return newSetFromMap(new ConcurrentHashMap());
     }
  2. Create a thread-safe Set using the incoming collection

    public static <E> Set<E> newConcurrentHashSet(Iterable<? extends E> elements) {
         //Create a ConcurrentHashSet
         Set set = newConcurrentHashSet();
         // Use method in Guava to add elements in set to set
         (set, elements);
         return set;
     }

Create LinkedHashSet

  1. Create a LinkedHashSet directly

    public static <E> LinkedHashSet<E> newLinkedHashSet() {
        return new LinkedHashSet();
    }
    
  2. Create a LinkedHashSet of the expected size

    public static <E> LinkedHashSet<E> newLinkedHashSetWithExpectedSize(int expectedSize) {
         //Return a LinkedHashSet with a size of 4/3 of expectedSize
         return new LinkedHashSet((expectedSize));
     }
  3. Return a LinkedHashSet based on the incoming collection

    public static <E> LinkedHashSet<E> newLinkedHashSet(Iterable<? extends E> elements) {
         //If it is a Collection type, create a LinkedHashSet directly and assign the value in the collection to the new LinkedHashSet
         if(elements instanceof Collection) {
             return new LinkedHashSet((elements));
         } else {
             LinkedHashSet set = newLinkedHashSet();
             (set, elements);
             return set;
         }
     }

Create TreeSet

  1. Create a TreeSet directly

    public static <E extends Comparable> TreeSet<E> newTreeSet() {
        return new TreeSet();
    }
    
  2. Pass in a collection, return a TreeSet, and assign elements in the collection to TreeSet

    public static <E extends Comparable> TreeSet<E> newTreeSet(Iterable<? extends E> elements) {
         //Create a TreeSet
         TreeSet set = newTreeSet();
         // Assign elements in the collection to TreeSet
         (set, elements);
         return set;
     }
  3. Pass in a Comparator and create a TreeSet according to the Comparator rules

    public static <E> TreeSet<E> newTreeSet(Comparator<? super E> comparator) {
        return new TreeSet((Comparator)(comparator));
    }
    

Create IdentityHashSet

Create an IdentityHashSet based on() and two methods

public static <E> Set<E> newIdentityHashSet() {
     // Create an IdentityHashMap using the() method, and then convert the Map to Set using the newSetFromMap method
     return newSetFromMap(());
 }

Create a CopyOnWriteArraySet

  1. Create a CopyOnWriteArraySet directly

    public static <E> CopyOnWriteArraySet<E> newCopyOnWriteArraySet() {
        return new CopyOnWriteArraySet();
    }
    
  2. Create a CopyOnWriteArraySet based on the incoming collection and assign the data in the collection to the CopyOnWriteArraySet

    public static <E> CopyOnWriteArraySet<E> newCopyOnWriteArraySet(Iterable<? extends E> elements) {
         //If it is a Collection, convert it directly into a Collection. If it is not, create a List using Lists.
         Object elementsCollection = elements instanceof Collection?(elements):(elements);
         return new CopyOnWriteArraySet((Collection)elementsCollection);
     }

Create an EnumSet

  1. Return an EnumSet based on the incoming set and a type

    public static <E extends Enum<E>> EnumSet<E> newEnumSet(Iterable<E> iterable, Class<E> elementType) {
         //Create a set according to the type passed in
         EnumSet set = (elementType);
         //Add elements from the collection to set
         (set, iterable);
         return set;
     }
  2. Pass in a collection and return an EnumSet

    public static <E extends Enum<E>> EnumSet<E> complementOf(Collection<E> collection) {
        if(collection instanceof EnumSet) {
            return ((EnumSet)collection);
        } else {
            (!(), "collection is empty; use the other version of this method");
            Class type = ((Enum)().next()).getDeclaringClass();
            return makeComplementByHand(collection, type);
        }
    }
    

Create a Set from a Map

Create a Set from Map

public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
     return (map);
 }
 //Method, chase up one layer after another, and finally you can see that the class is actually used:

 private static class SetFromMap<E> extends AbstractSet<E> implements Set<E>, Serializable{
     private final Map<E, Boolean> m; // The backing map
     private transient Set<E> s; // Its keySet
     SetFromMap(Map<E, Boolean> map) {//In fact, it is to combine the key set in the map into a Set
         if (!())
             throw new IllegalArgumentException("Map is non-empty");
         m = map;
         s = ();
     }
 }

Take the non-overlapping part of the two sets as the view

Pass in two Sets, return one and two set1 that do not contain elements in set2

public static <E> <E> difference(final Set<E> set1, final Set<?> set2) {
     (set1, "set1");
     (set2, "set2");
     //Create a filter rule (the rule is, cannot contain elements in set2)
     final Predicate notInSet2 = ((set2));
     return new (null) {
         //Rewrite the iterator and use an iterator that does not contain set2 elements to filter out an iterator
         public Iterator<E> iterator() {
             return ((), notInSet2);
         }
         // Calculate the length based on the final returned iterator
         public int size() {
             return (());
         }
         //If both set1 and set2 are equal, it is empty
         public boolean isEmpty() {
             return (set1);
         }
         public boolean contains(Object element) {
             return (element) && !(element);
         }
     };
 }

Take the union of two sets as view

public static <E> <E> union(final Set<? extends E> set1, final Set<? extends E> set2) {
     (set1, "set1");
     (set2, "set2");
     //Get set2, all elements of set1 are not included in it
     final set2minus1 = difference(set2, set1);
     return new (null) {
         //Get the full length of set1 and the length of set2minus1 view
         public int size() {
             return () + ();
         }
         public boolean isEmpty() {
             return () && ();
         }
         public Iterator<E> iterator() {
             return (((), ()));
         }
         public boolean contains(Object object) {
             return (object) || (object);
         }
         //Return all elements
         public <S extends Set<E>> S copyInto(S set) {
             (set1);
             (set2);
             return set;
         }
         public ImmutableSet<E> immutableCopy() {
             return (new Builder()).addAll(set1).addAll(set2).build();
         }
     };
 }

Take the intersection of two sets as view

public static <E> <E> interference(final Set<E> set1, final Set<?> set2) {
     (set1, "set1");
     (set2, "set2");
     //Create a filtering rule (rule is: all set2 elements)
     final Predicate inSet2 = (set2);
     return new (null) {
         //Returns set1 contains all elements in set2
         public Iterator<E> iterator() {
             return ((), inSet2);
         }
         // Calculate the length based on the calculated iterator
         public int size() {
             return (());
         }
         //Judge whether it is empty based on the iterator
         public boolean isEmpty() {
             return !().hasNext();
         }
         public boolean contains(Object object) {
             return (object) && (object);
         }
         public boolean containsAll(Collection<?> collection) {
             return (collection) && (collection);
         }
     };
 }

Take the symmetrical parts of two Sets as the view

public static <E> <E> symmetricDifference(Set<? extends E> set1, Set<? extends E> set2) {
    (set1, "set1");
    (set2, "set2");
    return difference(union(set1, set2), intersection(set1, set2));
}

Filter Set

Set filtering is similar to the various filtering implemented in Maps.

Pass in a Set and a filtering rule, and return a filtered Set:

public static <E> Set<E> filter(Set<E> unfiltered, Predicate<? super E> predicate) {
     //If the incoming Set is of SortedSet type, use the method of passing in SortedSet for processing
     if(unfiltered instanceof SortedSet) {
         return filter((SortedSet)((SortedSet)unfiltered), predict);
     } else if(unfiltered instanceof ) {
          filtered = ()unfiltered;
         Predicate combinedPredicate = (, predict);
         return new ((Set), combinedPredicate);
     } else {
         return new ((Set)(unfiltered), (Predicate)(predicate));
     }
 }

Previous recommendations

  • "SpringBoot" EasyExcel implements the import and export of millions of data
  • "SpringBoot" The most complete SpringBoot commentary in history
  • Detailed explanation of Spring Framework IoC Core
  • A long article of ten thousand words will take you to explore all the expansion points in Spring
  • How to implement a general interface current limiting, heavy-weight and anti-shake mechanism
  • A long article of ten thousand words will take you into the underlying data structure of Redis
  • Analysis of the most comprehensive principle of volatile keyword