Guava SortedSetMultimap doesn't put unordered values

1.3k views Asked by At

I want to create a TreeMultimap, but only the first value is added as a value. Here's my code:

SortedSetMultimap<Double, String> sorted = TreeMultimap.create(
    (o1, o2) -> o2.compareTo(o1), 
    (o1, o2) -> 0
);

allSuperSkuRepository.getAllSkuTitles().forEach(v -> {
    String sku = v.getSkuTitle().split(" - ")[0];
    Double lev = (double) levensteinDistance.getDistance(in, sku);
    if (lev.compareTo(0.3) > 0) lev = .5; // just for testing
    if (lev.compareTo(0.49) > 0) {
        log.debug(lev);
        log.debug(sku);
        sorted.put(lev, sku);
    }
});
log.debug(sorted); // {0.5=[s0104]}

As commented in the code, log.debug(sorted) only yields {0.5=[s0104]}, however the expected result would be {0.5=[s0104, msr01, msr01-set]}.

The log.debug(lev), log.debug(sku) are outputting the expected values.

What do I need to change in order to get all values for a key in my Multimap?

I'm using guava 19.


Important to note:

If I rewrite the comparator to

SortedSetMultimap<Double, String> sorted = TreeMultimap.create(
    (o1, o2) -> o2.compareTo(o1), 
    (o1, o2) -> o2.compareTo(o1)
);

all the values are actually added, but I don't want the values to be ordered in any way - they should appear in order of insertion.

1

There are 1 answers

2
Frank Pavageau On BEST ANSWER

The value comparator that you pass to the TreeMultimap factory says that all values are equal:

(o1, o2) -> 0

Hence, only the first one is retained. As the documentation says (emphasis mine):

Implementation of Multimap whose keys and values are ordered by their natural ordering or by supplied comparators. In all cases, this implementation uses Comparable.compareTo(T) or Comparator.compare(T, T) instead of Object.equals(java.lang.Object) to determine equivalence of instances.

What you really want is a custom Multimap, since none of the predefined combinations matches your use case:

SetMultimap<Double, String> sorted = Multimaps.newSetMultimap(
    new TreeMap<>(),
    LinkedHashSet::new);

The TreeMap will make sure the keys are sorted (using their natural ordering), and the LinkedHashSet for each collection of values will simply retain the insertion order while deduplicating the values.

If you don't have duplicate values, just use an ArrayList instead:

Multimap<Double, String> sorted = Multimaps.newMultimap(
    new TreeMap<>(),
    ArrayList::new);

Update: Louis Wasserman appropriately suggested in a comment using the MultimapBuilder fluent API instead to create the Multimap to specs:

Multimap<Double, String> sorted = MultimapBuilder.treeKeys().linkedHashSetValues().build();

or

Multimap<Double, String> sorted = MultimapBuilder.treeKeys().arrayListValues().build();