【问题标题】:How can I permute a generic list in Java?如何在 Java 中置换通用列表?
【发布时间】:2014-11-07 07:53:00
【问题描述】:

我正在尝试编写一个函数来置换一个通用列表,但是发生了一些非常奇怪的事情。如果我用数组替换所有列表,则以下代码结构有效,但因为它是代码,所以只需打印出 [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);
    }
}

【问题讨论】:

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


    【解决方案1】:

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

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

    因此,不知何故,相同的引用被添加到集合中六次,并带有六个不同的哈希码。但它们仍然都是对同一个List 的引用,这意味着当您将它们打印出来时,它们看起来都一样。

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

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

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

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

    【讨论】:

    • 在“permute(newLst, start + 1, perms);”上方添加一行两次交换之间修复它。谢谢!
    猜你喜欢
    • 2010-09-08
    • 1970-01-01
    • 2016-01-18
    • 2012-08-22
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    相关资源
    最近更新 更多