3

I have a map-like type SmurfMap<K, V> which in certain contexts I use as a set-like SmurfMap<K, Void>. Implementation details forbid the values of the map from being null, so I cannot actually use Void as a unit type. Are there any non-null alternatives to Void in the JCL or Guava?

Types considered and not yet rejected:

  • new singleton enum Unit
  • Class<Void>
  • TypeToken<Void>
  • Object

EDIT SmurfMap<K, V> does not implement java.util.Map. The datastructure backing a SmurfMap is terabytes to petabytes in size.

  • 1
    Standard Java libraries use Boolean (Set<T> set = Collections.newSetFromMap(Map<T, Boolean> map)) – Ordous May 26 '15 at 16:19
  • Is K actually an enum? – user40980 May 26 '15 at 16:20
  • @Ordous Boolean seems odd. What's the difference between a map which does not contain the key "fish" and a map where the key "fish" go to false? – Robert Cooper May 26 '15 at 16:26
  • 1
    @RobertCooper The value is never looked at, only the presence or absence of such. Hey, I didn't write java.util, don't blame me for quirky stuff there! – Ordous May 26 '15 at 16:29
  • 3
    So you're asking what's a suitable way of iterating through the keys of SmurfMap, which doesn't implement java.util.Map and therefore no keySet() to rely on? FWIW java.util.HashSet is backed by java.util.HashMap and uses private static final Object PRESENT = new Object(); as the value. – h.j.k. May 27 '15 at 6:46
4

If you don't need the values, don't use a map. Use a Set. It's a collection that contains no duplicate elements (by equals and hashCode) and has O(1) membership checking.

If the code is impossible to structure in a way that uses a set, and it must use a map*, then of these options I'd make a singleton Unit type. I'd pick this because as a map value, it does the best job communicating that the value is unused. A Map<T, Boolean> implies that the boolean-ness of the values matters and should be checked.

* This indicates a pretty nasty code smell, and you should examine your design more.

  • 2
    I think you have the situation backwards - seems the OP already has a custom Map, and doesn't want to duplicate the code into a almost-identical Set. (Same reason there are no ConcurrentSets in Java). Hence this is not a question of "I want a set, what should I use? I've got a Map.", but rather "I've got this Map, I'd like to reuse the code as a Set, but without copy-pasting". – Ordous May 26 '15 at 16:54
  • @Ordous I'm still answering the first question - if you have a map that you pass around, and the values are being used, there's no need to even ask the question because you just use map.contains. If you have a map and you never use the values, you shouldn't be using a map. If you have a map, never use the values, and can't replace it with a set, it's a code smell as I said and in that case I recommended the Unit singleton value. I think this pretty solidly answers all aspects of the question. – Daenyth May 26 '15 at 17:00
  • But then your answer collapses into "Use a Unit type!" with no argument or reason for it. – Ordous May 26 '15 at 17:04
  • 3
    @Ordous: Hence this is not a question of "I want a set, what should I use? I've got a Map.", but rather "I've got this Map, I'd like to reuse the code as a Set, but without copy-pasting". And the answer is still valid: "don't do that." This looks like a classic case of "when all you have is a hammer..." – Mason Wheeler May 26 '15 at 20:59
  • 1
    @MasonWheeler ConcurrentSkipListSet<E> is implemented on top of a ConcurrentNavigableMap<E,Object>, HashSet<E> is implemented on top of HashMap<E, Object>. I'm doing something similar. I'd like to avoid the weight of a custom SmufSet<K> interface if it's easy. – Robert Cooper May 27 '15 at 5:18
1

You said in the comments that "SmurfMap is a map-like abstraction over a petabyte sized data structure". That means that storage space should be a major concern.

Given that background, I would use a custom type with only one value, which serialises to zero bytes. Go through the SmurfMap implementation to make sure that that will actually result in the least amount of storage space needed.

0

Two alternative techniques to consider:

  • Use a type with the Void generic argument removed. Instead of a Callable<Void>, use a Runnable. Instead of a SmurfMap<K, Void>, use (or make) a SmurfSet<K>

  • Modify the clients to be null-friendly. For example, I know of no good alternative to Future<Void>. Guava's Futures.allAsList() returns an unmodifiable wrapped ArrayList, not an ImmutableList, quite possibly in part because a the null-hostile ImmutableList would prevent the method from being called with Future<Void>s.

  • Is there any reason why you are proposing some concurrency-related classes in your own answer? If so, are they missing from your original question? – h.j.k. May 27 '15 at 23:58
  • 2
    My question was "Non-null alternative to Void (Java unit type)". I gave a specific case about a SmurfMap, but my question in general was about what to do you do when you'd like to use Void as a type parameter but you can't use null values. Callable, Runnable, and Future were random examples to illustrate different workarounds. The fact that they are all concurrency related was a coincidence (I could just as easily used Supplier or Function for examples). – Robert Cooper May 28 '15 at 3:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.