如何洗牌除元素外的列表?

How to shuffle list except an element?

我有一个包含值元素的列表整数:0, 7, 2, 1, 6, 5.

我知道我可以使用方法

Collections.shuffle(list);

洗牌我的名单。但我不想改变第二个位置的值。它应该总是 7。

我该怎么做?

您可以将 Collection 打乱,然后将 7 恢复到第二位:

Collections.shuffle(list);
list.set(list.indexOf(7),list.get(2));
list.set(2,7);

或更短:

Collections.shuffle(list);
Collections.swap(list, 2, list.indexOf(7));

正如其他人所建议的那样,您还可以在改组之前删除您希望保留其位置的元素,然后再将其添加到同一位置。

ArrayLists 的两种方法应该花费相似的时间(在最坏的情况下是线性的),因为在我的回答中 indexOf 将花费线性时间,但是在替代解决方案中删除和添加一个元素(尤其是如果索引接近列表的开头)将花费 ArrayList 的线性时间,因为必须将 removed/added 索引之后的所有元素推送到新索引。

简单地防止移动任意数量的元素

  • 将他们从列表中删除,
  • 随机播放其余元素,
  • 将它们放回原来的位置(从左开始以避免元素向右移动的问题)。

有一个更长的替代解决方案,但对于长列表可能更快:

public static <T> void shuffleExcept(final List<T> list, final int position) {
    List<T> view = new AbstractList<T>() {
        @Override
        public T get(int index) {
            return list.get(index >= position ? index+1 : index);
        }

        @Override
        public T set(int index, T element) {
            return list.set(index >= position ? index+1 : index, element);
        }

        @Override
        public int size() {
            return list.size()-1;
        }
    };
    Collections.shuffle(view);
}

这里我们创建了一个 "view" 原始列表,它是除了我们要保留的元素之外的整个列表(我们只是移动了后续元素的索引)。接下来我们洗牌这个视图。这就是接口的美妙之处:您可以要求现有方法做一些不同的事情,只需传递新的接口实现即可。

用法示例:

List<Integer> input = Arrays.asList(0, 7, 2, 1, 6, 5);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);

典型输出:

[6, 7, 5, 2, 1, 0]
[5, 7, 6, 1, 2, 0]
[6, 7, 2, 0, 5, 1]
[6, 7, 2, 0, 5, 1]
[2, 7, 0, 5, 6, 1]
[6, 7, 0, 2, 5, 1]
[5, 7, 2, 0, 1, 6]