简体   繁体   English

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

[英]How can I permute a generic list in Java?

I am trying to write a function to permute a generic list, but something very strange is happening. 我正在尝试编写一个置换通用列表的函数,但是正在发生一些非常奇怪的事情。 The following code-structure works if I replace all the lists with arrays, but as it is the code simply prints out [1, 2, 3] six times. 如果我将所有列表替换为数组,则以下代码结构有效,但由于该代码仅打印了六,[1,2,3]。 Why is this? 为什么是这样? I think it has something to do with pass by value vs pass by reference. 我认为这与按值传递与按引用传递有关。

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

When you call permute , you never create a new List . 调用permute ,您永远不会创建新的List lst is a reference to the List , and that reference gets passed around to all your recursive invocations, and the result is that you add the same reference to the set 6 times. lst是对List的引用,并且该引用传递给所有递归调用,其结果是您将相同的引用添加到集合6次。 Normally, adding the same reference to the set 6 times means that your Set will contain only one element. 通常,对集合添加相同的引用6次意味着您的Set将仅包含一个元素。

The reason it appears to have six elements at all is because you modify the contents of the List after you add it to the hash, something you're never supposed to do with a HashMap or HashSet . 它似乎有六个元素的原因是因为您在将List添加到哈希之后修改了List的内容,这是您永远都不应该使用HashMapHashSet If you add an object reference to a HashMap or HashSet , and later modify the object in a way that changes the hash code, you will screw up the workings of the hash. 如果将对象引用添加到HashMapHashSet ,然后以更改哈希码的方式修改该对象,则会搞乱哈希的工作。

Because of that, somehow the same reference got added to the set six times with six different hash codes. 因此,将相同的引用以六种不同的哈希码六次添加到集合中。 But they're still all a reference to the same List , which means that when you print them out, they will all appear the same. 但是它们仍然都是对同一List的引用,这意味着当您将它们打印出来时,它们都将显示为相同。

You need to make a copy of the List before you call permute recursively. 您需要复制List然后递归调用permute If it's OK for permute to know that the List is an ArrayList , you can say something like 如果permute知道ListArrayList是可以的,则可以说类似

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

and then do the first swap using newList , and then pass newList to your recursive invocation. 然后使用newList进行第一次swap ,然后将newList传递给递归调用。 (The second swap is probably no longer needed.) (可能不再需要第二次swap 。)

If you want to create a newList whose type is the same type as the source list ... I'm not sure if there's a simple way to do that, other than by using reflection. 如果您要创建一个newList其类型与源列表的类型相同...我不确定是否有简单的方法可以做到,除了使用反射。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM