Search code examples
javacollectionsclonedefensive-copy

Making a general copy of a Set<String> in constructor


I did not find a direct answer to this specific question, so... Assume class:

class MyClass {
    private final Set<String> tags;

    MyClass(Set<String> initialTags) {
        this.tags = ???(initialtags);
    }
}

I just want a copy without caring about exact implementation of the set, with minimal overhead, so basically a direct copy of whatever internal state initialTags has. I can't use clone, since Set is interface. Is this possible, or do I have to use something like new HashSet<String>(tags);, needing to make a decision about the type?

Assumptions: Set item type is resticted to immutable. I can trust the caller to not pass a broken initialTags Set implementation, and I can trust it to have good enough performance for any internal use in this class.

I'm thinking of trying reflection and clone(), but this seems a bit dirty...


Solution

  • Guava provides a method for that:

      /**
       * Returns an immutable set containing the given elements, in order. Repeated
       * occurrences of an element (according to {@link Object#equals}) after the
       * first are ignored. This method iterates over {@code elements} at most once.
       *
       * <p>Note that if {@code s} is a {@code Set<String>}, then {@code
       * ImmutableSet.copyOf(s)} returns an {@code ImmutableSet<String>} containing
       * each of the strings in {@code s}, while {@code ImmutableSet.of(s)} returns
       * a {@code ImmutableSet<Set<String>>} containing one element (the given set
       * itself).
       *
       * <p>Despite the method name, this method attempts to avoid actually copying
       * the data when it is safe to do so. The exact circumstances under which a
       * copy will or will not be performed are undocumented and subject to change.
       *
       * @throws NullPointerException if any of {@code elements} is null
       */
      public static <E> ImmutableSet<E> copyOf(Iterable<? extends E> elements) {
        return (elements instanceof Collection)
            ? copyOf(Collections2.cast(elements))
            : copyOf(elements.iterator());
      }