Question

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...

Was it helpful?

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());
  }

OTHER TIPS

Depending on what the expected size of initialTags is you may prefer different implementations; the code below shows quicker times for constructor-based copies when i is < 100000, but clone is faster when i > 1000000. If you really want to use clone you could always use isinstance to determine which of the (6) well known concrete implementations (that implement cloneable) it is, beyond that you have no guarantee initialTags has it.

static Set<String> byCloning(HashSet<String> initialTags) {

    return (Set<String>) initialTags.clone();
}

static Set<String> byConstructor(Set<String> initialTags) {

    return new HashSet<String>(initialTags);
}

public static void main(String[] args) {

    int N = Integer.parseInt(args[0]);

    HashSet<String> mySet = new HashSet<String>();
    for (int n = 0 ; n < N ; n++) mySet.add(Integer.toString(n));

    long start = System.currentTimeMillis();
    byCloning(mySet);
    long end = System.currentTimeMillis();
    System.out.println(" time take in milli seconds for cloning = " + (end-start) );

    start = System.currentTimeMillis();
    byConstructor(mySet);
    end = System.currentTimeMillis();
    System.out.println(" time take in milli seconds by constructor = " + (end-start) );
}

Either make a decision on the type based on the kind of access you need of the set, or you could try using Collections.unmodifiableSet() :

class MyClass {
    private final Set<String> tags;

    MyClass(Set<String> initialTags) {
        this.tags = Collections.unmodifiableSet(initialTags);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top