介绍
这篇博文用来记录我平常想到的有关Java有趣或者重要的东西
有序集合打乱
我做过一道面试题,Java语言设计一个扑克牌游戏,发牌,洗牌。
我写Android客户端代码,数据集合一般都是服务器已经处理好了的。还从来没想过怎么把有序的集合打乱。
当时脑子里面就想到for循环里,一个Random随机对象生成随机数。然后就懵逼了。
今天突然想到这个问题就看了一下有关Java集合的源码。
首先这样的代码,就可以生成随机数集合,然后排序,再然后打乱。
LinkedList<Integer> integers=new LinkedList<>();
Random random=new Random();
for (int i = 0; i < 10; i++) {
integers.add(random.nextInt(50));
}
for (Integer e: integers) {
OutUtils.outT(e+" ");
}
OutUtils.outlnT("随机生成的数");
Collections.sort(integers);
for (Integer e: integers) {
OutUtils.outT(e+" ");
}
OutUtils.outlnT("排序");
Collections.shuffle(integers);
for (Integer e: integers) {
OutUtils.outT(e+" ");
}
OutUtils.outlnT("打乱");
打印结果:
平常开发如果有需要打乱集合这样的代码也就够了,Collections.shuffle(集合对象)
静态方法已经够用了。
如果面试要求不能用集合框架的现成方法呢?或者是我们自己编写的集合对象呢?
哪就去看源码,学习源码是怎么样实现的。关键步骤有注释。
public static void shuffle(List<?> list, Random random) {
@SuppressWarnings("unchecked") // we won't put foreign objects in
final List<Object> objectList = (List<Object>) list;
//如果是 随机访问集合
if (list instanceof RandomAccess) {
for (int i = objectList.size() - 1; i > 0; i--) {
int index = random.nextInt(i + 1);
//先从随机的索引得到新值,从集合最后元素开始,新值覆盖原值,又从set返回的原值,放回索引。
objectList.set(index, objectList.set(i, objectList.get(index)));
}
} else {
//如果不是先转数组
Object[] array = objectList.toArray();
for (int i = array.length - 1; i > 0; i--) {
//数组没有set返回原值 就需要一个临时变量 实现元素互换
int index = random.nextInt(i + 1);
Object temp = array[i];
array[i] = array[index];
array[index] = temp;
}
int i = 0;
ListIterator<Object> it = objectList.listIterator();
//把已经打乱好的数组 通过迭代器,放回原集合
while (it.hasNext()) {
it.next();
it.set(array[i++]);
}
}
}
源码要考虑的情况比较多,相应的代码量就多。
本质就都是根据集合大小生成随机索引,这样就限定了随机数的范围不会超出集合或者数组索引范围发生异常,然后根据索引从右到左交换元素。
另记:
索引异常
ArrayIndexOutOfBoundsException:用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
IndexOutOfBoundsException:指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
其实随机数这个对象我也不熟悉,看一下random.nextInt()
API说明
Returns a pseudo-random uniformly distributed {@code int}
in the half-open range [0, n).
返回半闭合区间值。
所以random.nextInt(array.length - 1+1)
肯定是不会超出索引范围的。
把上面的生成随机索引方法抽出来,单独运行代码,看运行的结果。
我这里假设一个5个元素的集合,看一下实际运行情况:
这是我抽出来比较多重复元素的情况。
考虑这样极端的情况:每一次生成的随机索引都是同一个值,假设都是0。哪就是第一个元素在最右边,其他元素集体左移。也是打乱了顺序。
再考虑:5个元素的集合有多少概率shuffle方法打乱不起作用。首先这样的条件就是每次生成的随机索引都刚好等于正在逐步自减的i
,没有发生实际的交换。具体就是1/(5*4*3*2)=1/120的概率。5个元素打乱不起作用的概率百分之一都不到。
记住上面的实现逻辑,万一哪天真用上了呢。