8.9 生成直方图

前面的代码有些重复,但只要范围数不多,这是完全可以接受的。然而,如果我们要计算每个分数出现的次数,就必须编写 100 行代码:

  1. int count0 = inRange(scores, 0, 1);
  2. int count1 = inRange(scores, 1, 2);
  3. int count2 = inRange(scores, 2, 3);
  4. ...
  5. int count99 = inRange(scores, 99, 100);

我们需要存储 100 个计数器,最好还能用索引来访问计数器。换言之,我们需要一个数组!

下面的代码片段创建了一个包含 100 个计数器的数组,每种可能的分数一个。它遍历分数,并用 inRange 计算每种分数出现的次数,并将结果存储在数组中:

  1. int[] counts = new int[100];
  2. for (int i = 0; i < counts.length; i++) {
  3. counts[i] = inRange(scores, i, i + 1);
  4. }

注意,我们在三个地方使用了循环变量 i:一处用作索引访问数组 counts,两处用于设置 inRange 的两个实参。这些代码可行,但在效率方面还有改进空间。这个循环每次调用 inRange 时都遍历整个数组。

更好的做法是只遍历数组一次;计算每个成绩所在的范围,并将相应的计数器加 1。下面的代码在只遍历数组一次的情况下生成直方图:

  1. int[] counts = new int[100];
  2. for (int i = 0; i < scores.length; i++) {
  3. int index = scores[i];
  4. counts[index]++;
  5. }

这个循环每次执行时,都选择数组 scores 中的一个元素,并将其用作索引来将数组 counts 中相应的元素加 1。因为这些代码只遍历数组 scores 一次,所以其效率高得多。