Skip to main content

Collections

Package dev.versatia.util.collection.

*OrThrow helpers​

MutableSet.addOrThrow, addAllOrThrow, MutableMap.putOrThrow, putAllOrThrow and Map.getOrThrow turn silent overwrites and missing keys into exceptions.

val registry = mutableMapOf<String, Handler>()
registry.putOrThrow("shop", ShopHandler()) // throws if "shop" is already registered
  • addAllOrThrow and putAllOrThrow check the whole input before touching the receiver, so a failed call leaves the collection unchanged.
  • They also report duplicates inside the input, not only clashes with what is already stored.
  • A stored null value counts as present (containsKey, never get(key) != null).
  • Iterable<Map<K, V>>.mergeOrThrow merges several maps and, when keys are present in more than one of them, throws once with every conflict; each MapConflict(key, existingValue, newValue) tells which.

cycle​

List.cycle() is an endless iterator going round the list: its elements in order, then from the first again, such as lines broadcast in turn.

val rotation = lines.cycle()
every(period) { VersatiaEngine.broadcast(text(rotation.next())) }

The list is copied, so later changes to it are not seen; an empty list throws IllegalArgumentException (Cannot cycle through an empty list), since there would be nothing to go round.

DeepMergeMap​

A MutableMap<K, Any?> that merges other maps into itself recursively, useful for layering a default configuration under a user one. For every key of the incoming map:

  1. Key absent in the destination: the value is deep-copied in. Nested maps become nested DeepMergeMaps, nested collections become lists.
  2. Both values are maps: they are merged recursively with the same rules.
  3. Both values are collections: the result is their concatenation.
  4. Anything else is a conflict, resolved by the MergeConflictStrategy given to the constructor:
StrategyResult
OVERRIDE (default)incoming value wins
KEEP_EXISTINGexisting value stays
COMBINE_INTO_LIST[existing, incoming]; a collection on either side is flattened into it
THROWIllegalStateException naming the dotted key path
val merged = DeepMergeMap<String>(conflictStrategy = MergeConflictStrategy.KEEP_EXISTING)
merged += userConfig.getValues(true)
merged += defaults.getValues(true) // fills the gaps, keeps the user's values

null is an ordinary value, not "absent". Incoming maps are never mutated. Use merge(other) or map += other to mutate in place and mergedWith(other) to get a copy. There is deliberately no plus operator: defining both plus and plusAssign makes += on a var ambiguous in Kotlin.