如何避免在增强的 for 循环之外声明变量

How to avoid declaring variable outside of enhanced for loop

提问人:Good Lord 提问时间:11/14/2023 更新时间:11/14/2023 访问量:94

问:

我想避免在增强的for循环之外使用变量索引,因为它在循环之后是无用的,并且会污染命名空间,但无法找到解决它的方法。

问题

int index = 0;        // I want to avoid using this variable outside the loop
for (List<Integer> bucket : buckets) {
    for (int el : bucket) {
        a[index++] = el;
    }
}

整个代码

package sort;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class BucketSort {
    private BucketSort() {
    }

    private static int getMax(int[] a) {
        int max = Integer.MIN_VALUE;

        for (int el : a) {
            max = Math.max(max, el);
        }

        return max;
    }

    public static void sort(int[] a, int k) {
        int M = getMax(a) + 1;

        List<List<Integer>> buckets = new ArrayList<>();
        for (int i = 0; i < k; i++) {
            buckets.add(new ArrayList<>());
        }

        for (int el : a) {
            int bucketIndex = (int) Math.floor(k * (double) el / M);
            buckets.get(bucketIndex).add(el);
        }

        for (List<Integer> bucket : buckets) {
            Collections.sort(bucket);
        }

        int index = 0;
        for (List<Integer> bucket : buckets) {
            for (int el : bucket) {
                a[index++] = el;
            }
        }
    }
}

一位用户在回答类似问题时建议,避免“无用变量”的一种方法是将另一个块包裹在整个地块周围以进行局部范围界定

{
    int index = 0;        
    for (List<Integer> bucket : buckets) {
        for (int el : bucket) {
            a[index++] = el;
        }
    }
}

但我不太确定。

Java for 循环 foreach 作用域

评论

4赞 Andrew S 11/14/2023
移动到名称良好的私有方法。(其他循环也是如此,因此无需读取代码即可清楚地了解每个步骤应该做什么。
0赞 Stoil Terziev 11/14/2023
尝试隐藏索引将比仅使用 fori 复杂得多
2赞 Mark Rotteveel 11/14/2023
你为什么担心这个?
0赞 Good Lord 11/14/2023
@MarkRotteveel引用了其中一个答案,“但是,我理解避免这种情况的愿望,因为未来的开发人员可能会推断出它正在其他地方使用。
0赞 Mark Rotteveel 11/14/2023
恕我直言,我不认为这是一个有效的担忧,否则这意味着您的方法做得太多了,需要分解为多种方法。

答:

2赞 wannaBeDev 11/14/2023 #1

一种方法是使用流:

a = buckets.stream().flatMap(List::stream).mapToInt(Integer::intValue).toArray();

评论

0赞 roksui 11/14/2023
请注意,您必须使用 Java 8 或更高版本才能使用 streams API。
0赞 user85421 11/14/2023
另请注意,这会导致一个新数组,该数组在从该方法返回时将丢失,也就是说,没有排序(Java 是按值传递的)
0赞 David Conrad 11/14/2023
@user85421好点,必须更改方法以返回数组而不是 void。
0赞 David Conrad 11/14/2023
@roksui自从 Java 21 刚刚发布以来,我希望没有多少人甚至没有使用 Java 8。
1赞 vsfDawg 11/14/2023 #2

首先,在循环之外创建变量实际上并没有错。但是,我理解避免这种情况的愿望,因为未来的开发人员可能会推断出它正在其他地方使用。

  1. 使用传统的 for 循环。您的 List 是一个 ArrayList,因此按索引迭代不是问题。
  2. 将逻辑移动到单独的方法中,因此变量的范围显然受到限制
  3. 使用 Stream API,如@wannaBeDev的答案中所示
2赞 WJS 11/14/2023 #3

只需将其粘贴在隐藏任何局部变量的方法中即可。

private void copy(int[] a, List<List<Integer>> buckets) {
    int index = 0;
    for (List<Integer> bucket : buckets) {
        for (int el : bucket) {
            a[index++] = el;
        }
    }
}

但是你试图避免一种非常常见的情况,即使是最好的编码员也必须接受。我会让它保持原样,或者如前所述扔掉它。{}