The semantic data structure you are looking for is often referred to as a bag. In the bag, as in the set, the order of the elements does not matter. However, in the bag, as in the list, duplicate items are allowed. Thus, the equality of the amounts consists of the fact that each of them has the same elements in the same quantities, although not necessarily in the same order. Thus, it seems that what you are looking for is a way to apply bag semantics to your list. The easiest way to do this is to duplicate one of the bags and remove the other bag elements from the duplicate until:
- all other elements of the bag are exhausted, and the duplicate is empty (they are equal!)
- all other elements of the bag are exhausted, and the duplicate is NOT empty (they are different!)
- during iteration, one of the other elements of the bag cannot be removed from the duplicate (they are different!)
Something like the equals() implementation shown below:
class Bag { List list Bag(List list) { this.list = list } @Override boolean equals(that) { def thisList = list?.clone() ?: [] that instanceof Bag && (that?.list ?: []).every { thisList.remove((Object)it) } && !thisList } @Override int hashCode() { this?.list?.sum { it?.hashCode() ?: 0 } ?: 0 } @Override String toString() { this?.list?.toString() } } def a = [1, 5, 1, -1, 8] as Bag def b = [5, 1, -1, 8, 1] as Bag
Alternatively, if you do not care about the initial order for the list at all, you can present the bag in the form of a card. The values โโof the summing element are the keys of the map, and the number of occurrences of each element of the bag are the values โโof the map. At this point, equality is simply equality of the map.
Like this:
class BagAsMap { Map map = [:] BagAsMap(List list) { (list ?: []).each { map[it] = (map[it] ?: 0) + 1 } } @Override boolean equals(that) { that instanceof BagAsMap && this?.map == that?.map } @Override int hashCode() { this?.map?.hashCode() ?: 0 } @Override String toString() { '[' + map.keySet().sum { k -> (0..<(map[k])).sum { "${k}, " } }[0..-3] + ']' } } def a1 = [1, 5, 1, -1, 8] as BagAsMap def b1 = [5, 1, -1, 8, 1] as BagAsMap
In any case, this is a serious search, if you just need to check the equivalence of the list one or two times, but if the semantics of the package are a frequent need, then defining the Bag class in one of these two ways is probably a good idea.
As noted elsewhere, in this particular case, a.sort() == b.sort() is a sufficient stop second instead of the full semantics of the package. However, not all objects that can be put together in the list are mutually sorted, even with the most difficult closing of the comparator. However, they all have hashCode() and equals() , which is all that is needed for the package implementation shown.
In addition, List.sort() has the algorithmic complexity O (n log n), while all List.sort() operations are shown as O (n). Do not worry about these small lists, but much more for large ones.