即使在我通过打印每个元素进行验证后,Iterable也会抛出NoSuchElementException

时间:2014-03-05 23:55:11

标签: java lazy-loading guava iterable nosuchelementexception

我有一堆Map<Integer, TreeSet<Double>>个对象,我将值传递给另一个方法。我在该方法中得到NoSuchElementException,因此开始调试,并看到对Map#values()的调用抛出此异常。但是,当我遍历值时,它会完美地打印所有值。以下是导致此问题的代码段:

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());

签名为getMeanPlot(Iterable<TreeSet<Double>>)

如果迭代在传递给第二个方法之前是如何正确的,但是在第二个方法启动时抛出异常?

堆栈跟踪

堆栈跟踪显示Maps.EntryTransformer对象ToRescaledValueMap是罪魁祸首。这是跟踪:

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

这与Guava的懒惰初始化有什么关系吗?我可以编写自己的小方法而不是使用Maps#transformEntries方法,但不知道这个bug背后的原因是......好吧......烦我。条目转换如下:

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;
        }
    };
}

修改

  • 我不知道这是否重要,但我想补充一点,我也发布了相同的打印语句(上面代码中的Iterable<TreeSet<Double>上的for循环)作为{{1的第一个语句}}。即使在那里,它打印正常,然后抛出异常。
  • 我对在变量名中使用下划线表示歉意。变量反映了我在代码背后的数学中使用的下标。我知道命名约定,但在这段代码中,我更专注于能够通过查看它来识别变量的数学含义。

1 个答案:

答案 0 :(得分:1)

实际上你打印TreeSet<Double> set这会误导你认为你的收藏已经填充。因为您的错误不在set而在TreeMultimap<String,Double> mmap上:您执行Ordering.natural().min(mmap),而这就是问题的来源。 Guava指定在空集合中查找min(或max)元素将导致抛出NoSuchElementException,这就是你在这里所拥有的。