Question

I have a bunch of Map<Integer, TreeSet<Double>> objects, and I am passing the values off to another method. I got a NoSuchElementException in that method, so started debugging, and saw that the call to Map#values() throws this exception. But, when I loop through the values, it prints all the values perfectly. Here's the code snippet that's causing this issue:

Map<Integer, TreeMultimap<String, Double>> kMap = Maps.newTreeMap();
for (int i = 0; i < 3; i++) {
    TreeMultimap<String, Double> treeMultimap = TreeMultimap.create();
    treeMultimap.putAll("a" + i, Lists.newArrayList(0.0, 0.06, 0.17, 0.23));
    treeMultimap.putAll("b" + i, Lists.newArrayList(0.0, 0.16, 0.34, 0.49));
    kMap.put(i, treeMultimap);
}

Map<Integer, TreeSet<Double>> rescaledKMap =
    Maps.transformEntries(kMap, ToRescaledValueMap(1.2));

Iterable<TreeSet<Double>> treeSets = rescaledKMap.values();

// Prints out each element correctly. No exception is thrown.
for (TreeSet<Double> set : treeSets)
    System.out.println(Joiner.on(',').join(set));

// NoSuchElementException thrown in getMeanPlot() before anything happens there.
TreeMap<Double, Double> mean_rescaledKMap = 
    getMeanPlot(rescaledKMap.values());

The signature is getMeanPlot(Iterable<TreeSet<Double>>).

How is the iterable correct just before it gets passed to the second method, but throws an exception just as the second method start?

Stack Trace

The stack trace shows the Maps.EntryTransformer object ToRescaledValueMap as the culprit. Here's the trace:

Exception in thread "main" java.util.NoSuchElementException
    at java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1113)
    at java.util.TreeMap$EntryIterator.next(TreeMap.java:1151)
    at java.util.TreeMap$EntryIterator.next(TreeMap.java:1146)
    at com.google.common.collect.AbstractMapBasedMultimap$Itr.next(AbstractMapBasedMultimap.java:1145)
    at com.google.common.collect.Ordering.min(Ordering.java:460)
    at com.google.common.collect.Ordering.min(Ordering.java:479)
    at DistributionMetricExperiment$6.transformEntry

Does this have anything to with Guava's lazy initialization? I can write my own little method instead of using the Maps#transformEntries method, but not knowing the reason behind this bug is ... well ... bugging me. The entry transformation is as follows:

static Maps.EntryTransformer<Integer, TreeMultimap<String,Double>, TreeSet<Double>>
ToRescaledValueMap(final double interval_length) {
    return new Maps.EntryTransformer<Integer, TreeMultimap<String,Double>, TreeSet<Double>>()
    {
        public TreeSet<Double> transformEntry(Integer i, TreeMultimap<String,Double> mmap) {
            double mmap_min = Ordering.natural().min(mmap.values());
            double mmap_max = Ordering.natural().max(mmap.values());
            TreeSet<Double> rescaledValues = Sets.newTreeSet();
            for (double val : mmap.values())
                rescaledValues.add(interval_length * (val - mmap_min)/(mmap_max - mmap_min));
            return rescaledValues;
        }
    };
}

Edit:

  • I don't know whether this matters or not, but wanted to add that I have also issued the same print statements (the for loop over Iterable<TreeSet<Double> in the above code) as the first statement of getMeanPlot. Even there, it prints alright, and then throws the exception.
  • My apologies for the use of underscore in variable names. The variables are reflecting subscripts I have been using in the maths behind my code. I am aware of the naming convention, but in this code I was more focused on having the ability to recognize the mathematical meaning of a variable just by looking at it.
Was it helpful?

Solution

Actually you print the TreeSet<Double> set and this misleads you into thinking that your collection is populated. Because your error is not on set but rather on TreeMultimap<String,Double> mmap: you execute Ordering.natural().min(mmap) and that's where the issue is coming from. Guava specifies that looking for the min (or max) element in an empty collection will result in throwing a NoSuchElementException, which is what you have here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top