集合小练习
练习一
需求:班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器。
本题的重要知识点在于快速向一个列表中添加批量的元素。然后在于如何实现随机访问列表中的元素,第一种实现方式可以是利用Random类生成一个随机索引,通过该索引得到元素;第二种方式是使用Collections工具类提供的shuffle方法,每次将列表中的元素顺序打乱,然后访问第一个元素(或者任意一个)即可。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class RandomAccess {
public static void main(String[] args) {
/*班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器。*/
// 1.定义列表
List<String> students = new ArrayList<>();
// 2.添加元素
Collections.addAll(students, "aaa", "bbb", "ccc", "ddd", "eee",
"fff", "ggg", "hhh", "iii", "jjj", "kkk");
// 3-1.通过Random对象随机访问
Random r = new Random();
int index = r.nextInt(students.size());
System.out.println(students.get(index));
// 3-2.通过Collections工具类访问
Collections.shuffle(students);
System.out.println(students.get(0));
}
}
练习二
班级里有N个学生
要求:
70%的概率随机到男生30%的概率随机到女生
本题的难点在于如何实现抽取的性别按概率分布。Random.nextInt(int bound)方法返回[0,bound)中的一个数(前包后闭),说白了就是随机选取0-99这100个数中的一个,这100个数字中的任意一个被抽中的概率都是相同的。因此可以使用Random.nextInt(100)<30来表示30%的概率,余下就是70%的概率。
可以先按性别定义两个列表,使用Random对象按30-70的概率选择访问女生-男生列表,再使用Collections.shuffle方法洗牌,选择0号索引的元素,即已经实现按指定概率抽取。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class RandomAccessWithOdds {
public static void main(String[] args) {
String name = "";
// ===使用方法1======================================
// 开始时间
long bt1 = System.currentTimeMillis();
int boyCount = 0;
int girlCount = 0;
for (int i = 0; i < 100000; i++) {
name = boyOrGirl1();
if (name.startsWith("b"))
boyCount++;
else if (name.startsWith("g"))
girlCount++;
}
System.out.println("boy被抽中:" + boyCount);
System.out.println("girl被抽中:" + girlCount);
System.out.println("耗时(ms):" + (System.currentTimeMillis() - bt1));
// ===使用方法2==================================
// 开始时间
long bt2 = System.currentTimeMillis();
int boyCount2 = 0;
int girlCount2 = 0;
for (int i = 0; i < 100000; i++) {
name = boyOrGirl1();
if (name.startsWith("b"))
boyCount2++;
else if (name.startsWith("g"))
girlCount2++;
}
System.out.println("boy被抽中:" + boyCount2);
System.out.println("girl被抽中:" + girlCount2);
System.out.println("耗时(ms):" + (System.currentTimeMillis() - bt2));
// ===使用方法3===================================
// 创建列表
List<String> boys = new ArrayList<>();
List<String> girls = new ArrayList<>();
// 向列表中添加元素
Collections.addAll(boys, "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10");
Collections.addAll(girls, "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8", "g9", "g10");
// 开始时间
long bt3 = System.currentTimeMillis();
int boyCount3 = 0;
int girlCount3 = 0;
for (int i = 0; i < 100000; i++) {
name = boyOrGirl3(boys, girls);
if (name.startsWith("b"))
boyCount3++;
else if (name.startsWith("g"))
girlCount3++;
}
System.out.println("boy被抽中:" + boyCount3);
System.out.println("girl被抽中:" + girlCount3);
System.out.println("耗时(ms):" + (System.currentTimeMillis() - bt3));
}
/**
* 使用Collections.Shuffle打乱列表实现随机抽取
* @return 名字
*/
public static String boyOrGirl1(){
// 创建列表
List<String> boys = new ArrayList<>();
List<String> girls = new ArrayList<>();
// 向列表中添加元素
Collections.addAll(boys, "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10");
Collections.addAll(girls, "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8", "g9", "g10");
// 创建一个Random对象,
// 用于概率抽取boys或girls
Random boyOrGirlRand = new Random();
// Random.nextInt(int bound)在参数指定内的[0-100)概率相等
// 因此<30的所有整数的概率就是30%
// 此外就是70%的概率,从而实现30%/70%的概率分布
if (boyOrGirlRand.nextInt(100) < 30){
// 洗牌
Collections.shuffle(girls);
// 抽取男孩第一个
//System.out.println(girls.get(0));
return girls.get(0);
}else {
// 洗牌
Collections.shuffle(boys);
// 抽取女孩第一个
//System.out.println(girls.get(0));
return boys.get(0);
}
}
/**
* 使用Random生成随机索引访问元素,从而实现随机选取
* 此方法相比使用Collections.shuffle效率大幅提高
* @return 名字
*/
public static String boyOrGirl2(){
// 创建列表
List<String> boys = new ArrayList<>();
List<String> girls = new ArrayList<>();
// 向列表中添加元素
Collections.addAll(boys, "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10");
Collections.addAll(girls, "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8", "g9", "g10");
// 创建一个Random对象,
// 用于概率抽取boys或girls
Random boyOrGirlRand = new Random();
// Random.nextInt(int bound)在参数指定内的[0-100)概率相等
// 因此<30的所有整数的概率就是30%
// 此外就是70%的概率,从而实现30%/70%的概率分布
if (boyOrGirlRand.nextInt(100) < 30){
return girls.get(new Random().nextInt(girls.size()));
}else {
return boys.get(new Random().nextInt(girls.size()));
}
}
/**
* 本方法不创建姓名列表
* 使用已创建的姓名列表,性能又可以提升很多
* @param boys 男生姓名列表
* @param girls 女生姓名列表
* @return 名字
*/
public static String boyOrGirl3(List<String> boys, List<String> girls){
// 创建一个Random对象,
// 用于概率抽取boys或girls
Random boyOrGirlRand = new Random();
// Random.nextInt(int bound)在参数指定内的[0-100)概率相等
// 因此<30的所有整数的概率就是30%
// 此外就是70%的概率,从而实现30%/70%的概率分布
if (boyOrGirlRand.nextInt(100) < 30){
return girls.get(new Random().nextInt(girls.size()));
}else {
return boys.get(new Random().nextInt(girls.size()));
}
}
}
本程序三次运行的结果:
第一次:
boy被抽中:69891
girl被抽中:30109
耗时(ms):45
boy被抽中:70021
girl被抽中:29979
耗时(ms):18
boy被抽中:69868
girl被抽中:30132
耗时(ms):10
第二次:
boy被抽中:70085
girl被抽中:29915
耗时(ms):44
boy被抽中:70027
girl被抽中:29973
耗时(ms):19
boy被抽中:69933
girl被抽中:30067
耗时(ms):10
第三次:
boy被抽中:70222
girl被抽中:29778
耗时(ms):43
boy被抽中:69919
girl被抽中:30081
耗时(ms):18
boy被抽中:70020
girl被抽中:29980
耗时(ms):10
练习三
自动点名器3
班级里有N个学生,要求:被点到的学生不会再被点到。但是如果班级中所有的学生都点完了,需要重新开启第二轮点名。
代码如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class RandomAccessRepeatly {
public static void main(String[] args) {
// 创建一个列表
List<String> list = new ArrayList<>();
// 用于存放被删除的元素,用于开启第二轮
List<String> list2 = new ArrayList<>();
// 添加元素
Collections.addAll(list, "唐玄装", "朱棣", "曹操", "刘备",
"孙权", "张飞", "李世明", "诸葛亮", "刘秀", "董卓");
// 获取列表大小
int size = list.size();
// 创建随机数生成对象
Random r = new Random();
// 外循环:指定抽取的轮数
for (int j = 1; j <= 10; j++) {
System.out.println("=======第" + j + "次=========");
// 内循环:随机抽取列表中的元素
for (int i = 0; i < size; i++) {
// 设置随机对象的种子
r.setSeed(System.nanoTime());
// 以size为界生成一个随机索引
int index = r.nextInt(list.size());
// 使用remove()方法,移除选中的元素;
// 该方法返回被删除的元素,使用name接受
String name = list.remove(index);
// 向list2中添加被删除的元素
list2.add(name);
// 输出name
System.out.println(name);
}
// 添加list2
list.addAll(list2);
// 清空list2
list2.clear();
}
}
}
练习四
TxT文件中事先准备好80个学生姓名,每个学生的名字独占一行。
要求1:每次被点到的学生,再次被点到的概率在原先的基础上降低一半。举例:80个学生,点名5次,每次都点到小A,概率变化情况如下:
第一次每人概率:1.25%。
第二次小A概率:0.625%。
其他学生概率:1.2579%
第三次小A概率:0.3125%。
其他学生概率:1.261867%
第四次小A概率:0.15625%。
其他学生概率:1.2638449%
第五次小A概率:0.078125%。
其他学生概率:1.26483386%
要求2:作弊要求,第三次点名一定是张三。
提示:本题需要用到集合,IO,权重随机算法,有基础的同学可以试试,0基础的同学等IO学完之后再做。
练习五
定义一个Map集合,键用表示省份名称province,值表示市city,但是市会有多个。添加完毕后,遍历结果格式如下:
江苏省=南京市,扬州市,苏州市,无锡市,常州市
湖北省=武汉市,孝感市,十堰市,宜昌市,鄂州市
河北省=石家庄市,唐山市,邢台市,保定市,张家口市
代码如下:
import java.util.*;
public class ProvinceNCities {
public static void main(String[] args) {
//Map<String, List<String>> pnc1 = new HashMap<>();
// 定义Map对象
HashMap<String, ArrayList<String>> pnc = new HashMap<>();
// 添加省份 江苏
ArrayList<String> js = new ArrayList<>();
js.add("南京市");
js.add("扬州市");
js.add("苏州市");
js.add("无锡市");
js.add("常州市");
// 添加省份 湖北
ArrayList<String> hb = new ArrayList<>();
hb.add("武汉市");
hb.add("孝感市");
hb.add("十堰市");
hb.add("宜昌市");
hb.add("鄂州市");
// 添加省份 河北
ArrayList<String> heb = new ArrayList<>();
heb.add("石家庄市");
heb.add("唐山市");
heb.add("邢台市");
heb.add("保定市");
heb.add("张家口市");
// 将数据放入HashMap
pnc.put("江苏省", js);
pnc.put("湖北省", hb);
pnc.put("河北省", heb);
//System.out.println(pnc);
// 通过EntrySet访问
Set<Map.Entry<String, ArrayList<String>>> entries = pnc.entrySet();
for (Map.Entry<String, ArrayList<String>> entry : entries) {
//System.out.println(entry.getKey());
// sj用户字符串拼接
StringJoiner sj = new StringJoiner(",", entry.getKey() + " = ", "");
// 遍历得到的城市名
for (String s : entry.getValue()) {
sj.add(s);
}
// 输出得到的最终字符串
System.out.println(sj);
}
}
}