如何在Java中置换通用列表?

时间:2014-09-13 05:21:40

标签: java generics pass-by-reference permutation pass-by-value

我正在尝试编写一个函数来置换通用列表,但是发生了一些非常奇怪的事情。如果我用数组替换所有列表,则以下代码结构有效,但因为代码只是打印出[1,2,3]六次。为什么是这样?我认为这与传递值和传递参考有关。

//Main.java
import java.util.ArrayList;
import java.util.Set;
import java.util.List;

public class Main {

    public static void main (String ... args) {
        ArrayList<Integer> AL = new ArrayList<Integer>();
        AL.add(1);
        AL.add(2);
        AL.add(3);

        Permute<Integer> perm = new Permute<Integer>();
        Set<List<Integer>> set = perm.listPermutations(AL);

        for (List<Integer> lst : set) {
            System.out.println(lst);
        }
    }
}

//Permute.java
import java.util.List;
import java.util.Set;
import java.util.HashSet;

public class Permute<E> {

    public Set<List<E>> listPermutations(List<E> lst) {
        Set<List<E>> perms = new HashSet<List<E>>();
        permute(lst, 0, perms);
        return perms;
    }

    private void permute(List<E> lst, int start, Set<List<E>> perms) {
        if (start >= lst.size()) {
            // nothing left to permute 
            perms.add(lst);
        }

        for (int i = start; i < lst.size(); i++) {
            // swap elements at locations start and i
            swap(lst, start, i);
            permute(lst, start + 1, perms);
            swap(lst, start, i);
        }
    }

    private void swap(List<E> lst, int x, int y) {
        E temp = lst.get(x);
        lst.set(x, lst.get(y));
        lst.set(y, temp);
    }
}

1 个答案:

答案 0 :(得分:1)

当您致电permute时,您永远不会创建新的Listlst是对List的引用,该引用会传递给所有递归调用,结果是您将相同的引用添加到集合中6次。通常,将相同的引用添加到集合中意味着您的Set将只包含一个元素。

它看起来有六个元素的原因是因为你在将List添加到哈希后修改了HashMap的内容,这是你永远不应该对{{1}做的事情。 }或HashSet。如果您将对象引用添加到HashMapHashSet,然后以更改哈希代码的方式修改对象,则会搞乱哈希的工作。

正因为如此,使用六个不同的哈希码以不同的方式将相同的引用添加到集合中六次。但它们仍然是对同一List的引用,这意味着当您将它们打印出来时,它们看起来都是一样的。

在递归调用List之前,您需要复制permute。如果permute能够知道ListArrayList,那么您可以说

List<E> newList = new ArrayList<>(lst);

然后使用swap执行第一个newList,然后将newList传递给递归调用。 (可能不再需要第二个swap。)

如果你想创建一个类型与源列表类型相同的newList ...我不确定是否有一种简单的方法可以做到这一点,除了使用反射。