我们需要明确的几个问题:
1、什么是快速失败?快速失败和安全失败有什么区别?
2、单列集合和双列集合的用法上作简单的归纳?
3、怎么分别用单列集合和双列集合实现斗地主的发牌?
4、部分底层源码的剖析?
一、快速失败(fail-fast) and 安全失败(fail-safe)
1、快速失败:
在迭代器遍历一个集合对象时,如果在这期间对集合的对象进行更改,那么就会产生“快速失败”。但是其实,快速失败不算是错误,它更可以说是迭代器在阻止对对象进行更改而报出的一种提示。
原理:
迭代器遍历时直接访问集合中的内容,在遍历过程中一直使用一个modCount变量,如果在遍历期间改变集合的内容,就会改变modCount值,当迭代器进行hashnext()/next()获取下一个元素时会先检查modCount变量是否异常,无异常就继续遍历,modCount变量发生变化之后就会产生异常,终止当前遍历,也就是产生了我们所说的“快速失败”。
出现场景:
java.util包下的集合都是快速失败的,不能在多线程下进行并发修改(迭代过程中修改)。
2、安全失败:
采用安全失败的集合容器,在遍历时不是直接访问集合内容,而是对原集合先进行拷贝,然后对拷贝的集合进行操作的。
原理:
由于迭代时先对集合的拷贝进行遍历,所以在遍历过程中对原集合的更改并不能被迭代器检测到所以不会出发Concurrent Modificatuin Exception。但即使他没有报错,迭代器访问的仍然是原来的集合,对于原数组的更改,迭代器并不知道。
出现场景:
java.util.concurrent包下的容器都是安全失败的,可用于多线程下的并发修改。
二、几种用法的归纳
1、contains()、containsKey()和containsValue()的用法:
- contains是List集合下的方法,用于判断集合中是否包含某个元素,其类型是boolean型;
- containsKey() 和containsValue()是Map集合下的方法,与contains用法相似。
public static void main(String[] args){
LinkedList link = new LinkedList();
link.add("A");
link.add("B");
link.add("C");
link.indexOf("B") ;
boolean flag = link.contains("A");
System.out.println(link.indexOf("B"));
System.out.println(flag);
HashMap map = new HashMap();
map.put("15","AA");
map.put("16","BB");
map.put("17","CC");
System.out.println(map.containsKey("15"));
System.out.println(map.containsValue("CC"));
}
2、List中常用用法:
上面的代码中还cue到了indexof()方法,他是返回该元素第一次出现的索引,返回值为int类型,如果几何中不存在该元素,就返回-1;与此相似的lastIndexof()方法,返回该元素最后一个出现的位置,若集合中不存在该元素,返回-1。
- 另外还有一个get()方法,它是根据索引下标来获取该下标对应的元素值。
link.get(1);//就是获取下标为1的元素,得到结果应该是B.
- 还有set(int index,E val)方法,它是用指定元素替换列表中指定位置的元素。
link.set(2,"F");//就是把集合中原来2位置的C变成F
- 还有subList(fromIndex,endIndex)方法,它返回值仍然是一个list集合,集合中的元素是从fromIndex(包括)到 endIndex(不包括)之间的元素。
link.subList(1,2);
System.out.println(link.subList(1,2));
- 最后是toArray()方法和toArray(T[] a)方法,toArray()返回按适当顺序包含列表中的所有元素的数组(从第一个元素到最后一个元素);toArray(T[] a)返回按适当顺序(从第一个元素到最后一个元素)包含列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
3、map中的常用方法: - 同样有get()方法,用于返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
map.get("16");
System.out.println(map.get("16"));
-put()是插入元素的方法,将指定的值与此映射中的指定键关联(可选操作)。
map.put("16","HH");//即key值为16的value值为HH
//key值不允许重复,出现一样的key值就会将前一个value值覆盖,最终也就显示一个key。
- size()返回键值对的个数
map.size();
- keySet()返回值类型是Set,返回此映射中包含的键的 Set 视图。
- entrySet()返回值类型是Set<Map.Entry<K,V>>,返回此映射中包含的映射关系的 Set 视图。
三、斗地主发牌的代码
1、单列集合实现发牌
import java.util.ArrayList;
import java.util.Collections;
public class DouDiZhu {
public static void main(String[] args) {
//准备扑克牌
ArrayList<String> poker = new ArrayList<>();
poker.add("大王");
poker.add("小王");
String[] colors = {"♠","♣","♥","♦"};
String[] numbers = {"A","K","Q","J","10","9","8","7","6","5","4","3","2"};
for(String col:colors){
for(String num:numbers){
poker.add(col+num);
}
}
System.out.println(poker);
//洗牌
Collections.shuffle(poker);
System.out.println(poker);
ArrayList player1 = new ArrayList();
ArrayList player2 = new ArrayList();
ArrayList player3 = new ArrayList();
ArrayList zhuozi = new ArrayList();
for(int i = 0;i < poker.size();i++){
String pai = poker.get(i);
if(i>=51){
zhuozi.add(pai);
}else if(i%3==0){
player1.add(pai);
}else if(i%3==1){
player2.add(pai);
}else if(i%3==2){
player3.add(pai);
}
}
System.out.println("小一"+player1);
System.out.println("小二"+player2);
System.out.println("小三"+player3);
System.out.println("桌子"+zhuozi);
}
}
2、双列集合实现斗地主发牌
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
public class DouDiZhu2 {
public static void main(String[] args) {
HashMap<Integer, String> poker = new HashMap<>();//存放扑克牌的集合
ArrayList<Integer> pokerIndex = new ArrayList<>();//放索引的集合
//准备牌
String[] col = {"♠", "♥", "♣", "♦"};
String[] num = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
int index = 0;
poker.put(index, "大王");
pokerIndex.add(index);
index++;
poker.put(index, "小王");
index++;
for (String color : col) {
for (String number : num) {
poker.put(index, color + number);
pokerIndex.add(index);
index++;
}
}
//洗牌
Collections.shuffle(pokerIndex);
// System.out.println(poker);
//发牌
ArrayList<String> play1 = new ArrayList<>();
ArrayList<String> play2 = new ArrayList<>();
ArrayList<String> play3 = new ArrayList<>();
ArrayList<String> dipai = new ArrayList<>();
for (int i = 0; i < pokerIndex.size(); i++) {
int dex = pokerIndex.get(i);
String pai = poker.get(dex);
if (i >= 51) {
dipai.add(pai);
} else if (i % 3 == 0) {
play1.add(pai);
} else if (i % 3 == 1) {
play2.add(pai);
} else if (i % 3 == 2) {
play3.add(pai);
}
}
//给牌排序
Collections.sort(dipai);
Collections.sort(play1);
Collections.sort(play2);
Collections.sort(play3);
//输出玩家的牌
System.out.println(play1);
System.out.println("熊大:" + play1);
System.out.println("熊二:" + play2);
System.out.println("光光:" + play3);
System.out.println("图图:" + dipai);
}
}
四、需要了解的底层源码
呃呃呃呃…好像太难写出来了,好多…看看理解一下吧,推荐看到的一篇超级详细的关于底层实现的博客:
https://blog.csdn.net/smxjant/article/details/82899430