Day 15
2019年5月11日。
这是我学习Java的第十五天。
这一天,我学到了以下的知识。
Set集合
Set,特点为元素唯一,是一个不包含重复元素的Collection(集合)
以下,讲解Set接口下的实现类
-
HashSet
底层数据结构是哈希表和数组,是元素为链表的数组
元素唯一,但不有序
集合元素可以是null
线程不安全
构造方法:HashSet()
:构造一个新的空Set,底层是HashMap,默认初始容量是16,加载银子是0.75格式
HashSet<T> hashSet = new HashSet<>();
特点
HashSet 保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。(Interger类和String类已经重写了这两个方法)
例如:用HashSet存储Student对象,在Student对象中重写这两个方法(可以用IDEA的快捷键Alt + Insert快速创捷)
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
-
LinkedHashSet
底层数据结构是哈希表和链表
链表保证有序,哈希表保证元素唯一
构造方法:LinkedHashSet()
:构造一个新的空Set,底层是HashMap,默认初始容量是16,加载银子是0.75格式
LinkedHashSet<T> linkedHashSet = new LinkedHashSet<>();
-
TreeSet
底层数据结构是二叉树
能够对元素进行排序
构造方法:TreeSet()
:构造一个新的空Set,根据元素的自然顺序进行排序格式
TreeSet<T> treeSet = new TreeSet<>();
特点
TreeSet对元素有两种排序方式:分为自然排序(空参构造)和比较器排序(有参构造)- 自然排序:使用TreeSet集合进行元素的自然排序,那么对元素有要求,要求这个元素必须实现Comparable接口,否则无法进行自然排序。而保证元素的唯一性是靠compareTo方法的返回值来确定如果返回0,表示两个元素相等,则不重复存储。
例如:用TreeSet存储Student对象,在Student对象中重写Comparble接口的CompareTo(T t)方法,根据该方法的返回值的正负0,来进行排序
- 自然排序:使用TreeSet集合进行元素的自然排序,那么对元素有要求,要求这个元素必须实现Comparable接口,否则无法进行自然排序。而保证元素的唯一性是靠compareTo方法的返回值来确定如果返回0,表示两个元素相等,则不重复存储。
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student s) {
//比较逻辑是按照年龄大小来排序
int num = this.age - s.age;
//当年龄相同不能说明他是同一个对象,还得比较姓名
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
//if(num==0){
// num=this.name.compareTo(s.name);
//}
return num2;
}
}
- 比较器排序:要求新建一个实现了Comparator接口的比较器类
例如:以上一个范例的Student对象进行编写,编写一个MyComparator类
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
//按照年龄来排
int num = s1.getAge() - s2.getAge();
int num2=num==0?s1.getName().compareTo(s2.getName()):num;
return num2;
}
}
Map集合
Map,特点可以存储将键映射到值的对象,且一个映射不能包含重复的键,每个键最多只能映射到一个值
首先,讲解Map接口下的方法
-
添加功能
V put(K key,V value)
:添加元素
- 这个其实还有另一个功能:替换。如果键是第一次存储,就直接存储元素,返回null
如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
-
删除功能
void clear()
:移除所有的键值对元素V remove(Object key)
:根据键删除键值对元素,并把值返回
-
判断功能
boolean containsKey(Object key)
:判断集合是否包含指定的键boolean containsValue(Object value)
:判断集合是否包含指定的值boolean isEmpty()
:判断集合是否为空
-
获取功能
Set<Map.Entry<K,V>> entrySet()
:返回一个键值对的Set集合V get(Object key)
:根据键获取值Set<K> keySet()
:获取集合中所有键的集合Collection<V> values()
:获取集合中所有值的集合
-
长度功能
int size()
:返回集合中的键值对的对数
-
遍历
- 获取所有键的集合
遍历键的集合,获取到每一个键
根据键找值 - 获取所有键值对对象的集合
遍历键值对对象的集合,获取到每一个键值对对象
根据键值对对象找键和值
例如:遍历一个存储着 数字(Integer)——字符(String) 对象的Map集合,分别使用两张遍历方式
- 获取所有键的集合
public class Test{
public static void main (String[] args) {
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "aaa");
hashMap.put(2, "bbb");
hashMap.put(3, "ccc");
//1.通过键的集合来取值
Set<Integer> integers = hashMap.keySet();
for (Integer key : integers) {
System.out.println(key + "===" + hashMap.get(key));
}
//2.通过键值对来遍历
//entrySet() 获取键值对 对象 放到Set集合里面去
Set<Map.Entry<Integer, String>> entries = hashMap.entrySet();
//K getKey ()
//返回与此项对应的键。
//V getValue ()
//返回与此项对应的值。
for (Map.Entry<Integer, String> entry : entries) {
//System.out.println(entry);
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"==="+value);
}
}
}
之后,讲解Map接口下的实现类
-
HashMap
允许插入null键 null值,要保证元素的唯一性,也要重写元素里的hashCode()方法和 equals()方法
格式
HashMap<K, V> hashMap = new HashMap<>();
-
LinklistedMap
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序
底层的数据结构是链表和哈希表,元素有序,并且唯一
元素的有序性由链表数据结构保证,唯一性由哈希表数据结构保证
Map集合的数据结构只和键有关
格式
LinkedHashMap<K, V> linkedHashMap = new LinkedHashMap<>();
-
TreeMap
TreeMap 键不允许插入null
键的数据结构是红黑树,可保证键的排序和唯一性
排序分为自然排序和比较器排序
线程是不安全的,效率比较高
格式
TreeMap<K, V> treeMap = new TreeMap<>();
Map集合的经典算法
-
统计字符出现的个数
需求:统计字符串中每个字符出现的次数
aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5) b(4) c(3) d(2) e(1)
代码如下public class Test { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入一段字符串"); String str = sc.nextLine() HashMap<Character, Integer> hashMap = new HashMap<>(); char ch; for (int i = 0; i < str.length() ; i++) { int num = 0; ch = str.charAt(i); for (int j = i; j < str.length(); j++){ if (ch == str.charAt(j)){ num ++; } hashMap.put(ch,num); } } System.out.println(hashMap); }}
-
集合嵌套
- HashMap 嵌套 ArrayList
public class MyTest {
public static void main(String[] args) {
/**
*
三国演义
吕布
周瑜
笑傲江湖
令狐冲
林平之
神雕侠侣
郭靖
杨过
*/
// HashMap 嵌套 ArrayList
ArrayList<String> sgList = new ArrayList<>();
sgList.add("吕布");
sgList.add("周瑜");
ArrayList<String> xaList = new ArrayList<>();
xaList.add("令狐冲");
xaList.add("林平之");
ArrayList<String> sdList = new ArrayList<>();
sdList.add("郭靖");
sdList.add("杨过");
sdList.add("尹志平");
LinkedHashMap<String, ArrayList<String>> maxMap = new LinkedHashMap<>();
maxMap.put("三国演义", sgList);
maxMap.put("笑傲江湖", xaList);
maxMap.put("神雕侠侣", sdList);
//遍历
/**
*
三国演义
吕布
周瑜
笑傲江湖
令狐冲
林平之
神雕侠侣
郭靖
杨过
*/
Set<String> keySet = maxMap.keySet();
for (String key : keySet) {
System.out.println(key);
ArrayList<String> list = maxMap.get(key);
for (String s : list) {
System.out.println("\t"+s);
}
System.out.println();
}
}
}
- HashMap 嵌套 HashMap
public class MyTest2 {
public static void main(String[] args) {
/* 基础班
张三 20
李四 22
就业班
王五 21
赵六 23
*/
//HashMap 嵌套 HashMap
HashMap<String, Integer> jc = new HashMap<>();
jc.put("张三", 20);
jc.put("李四", 22);
HashMap<String, Integer> jy = new HashMap<>();
jy.put("王五", 21);
jy.put("赵六", 23);
HashMap<String, HashMap<String, Integer>> maxMap = new HashMap<String, HashMap<String, Integer>>();
maxMap.put("基础班", jc);
maxMap.put("就业班", jy);
//遍历集合
Set<String> keySet = maxMap.keySet();
for (String s : keySet) {
System.out.println(s);
HashMap<String, Integer> minMap = maxMap.get(s);
Set<String> minKey = minMap.keySet();
for (String s1 : minKey) {
System.out.println("\t" + s1 + "\t" + minMap.get(s1));
}
System.out.println();
}
System.out.println("------------------------");
Set<Map.Entry<String, HashMap<String, Integer>>> entries = maxMap.entrySet();
for (Map.Entry<String, HashMap<String, Integer>> en : entries) {
String key = en.getKey();
System.out.println(key);
HashMap<String, Integer> minMap = en.getValue();
Set<Map.Entry<String, Integer>> entries1 = minMap.entrySet();
for (Map.Entry<String, Integer> stringIntegerEntry : entries1) {
String key1 = stringIntegerEntry.getKey();
Integer value = stringIntegerEntry.getValue();
System.out.println("\t"+key1+" "+value);
}
System.out.println();
}
}
}
- ArrayList 嵌套 HashMap
public class MyTest3 {
public static void main(String[] args) {
//周瑜-- - 小乔
//吕布-- - 貂蝉
//
//郭靖-- - 黄蓉
//杨过-- - 小龙女
//
//令狐冲-- - 任盈盈
//林平之-- - 岳灵珊
//ArrayList 嵌套 HashMap
HashMap<String, String> sgMap = new HashMap<>();
sgMap.put("周瑜", "小乔");
sgMap.put("吕布", "貂蝉");
HashMap<String, String> sdMap = new HashMap<>();
sdMap.put("郭靖", "黄蓉");
sdMap.put("杨过", "小龙女");
HashMap<String, String> xaMap = new HashMap<>();
xaMap.put("令狐冲", "任盈盈");
xaMap.put("林平之", "岳灵珊");
ArrayList<HashMap<String, String>> maxList = new ArrayList<>();
maxList.add(sgMap);
maxList.add(sdMap);
maxList.add(xaMap);
for (HashMap<String, String> map : maxList) {
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "-----" + value);
}
System.out.println();
}
}
}
- 斗地主 发牌和洗牌
需求:模拟斗地主洗牌和发牌,牌没有排序
代码如下
public class MyTest2 {
public static void main(String[] args) {
//得有一副牌
HashMap<ArrayList<Integer>,String> pokerBox = new HashMap<>();
ArrayList<Integer> arrayList = new ArrayList<>();
//生成54张牌放到牌盒里面
String[] colors = {"♠", "♥", "♦", "♣"};
String[] nums = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
int index = 0;
for (String color : colors) {
for (String num : nums) {
arrayList.add(index);
pokerBox.put(arrayList,color.concat(num));
index ++;
}
}
//手动添加大小王
arrayList.add(52);
pokerBox.put(arrayList,"★");
arrayList.add(53);
pokerBox.put(arrayList,"☆");
//洗牌
Collections.shuffle(arrayList);
Collections.shuffle(arrayList);
Collections.shuffle(arrayList);
System.out.println(arrayList);
//发牌
TreeSet<Integer> 高进 = new TreeSet<>();
TreeSet<Integer> 刀仔 = new TreeSet<>();
TreeSet<Integer> 星仔 = new TreeSet<>();
TreeSet<Integer> 底牌 = new TreeSet<>();
// 高进 = (ArrayList<String>) pokerBox.subList(0,17);
// 高进 0 3 6 9
//刀仔 1 4 7 10
// 星仔 2 5 8 11
for (int i = 0; i < pokerBox.size(); i++) {
if (i >= pokerBox.size() - 3) {
底牌.add(arrayList.get(i));
} else if (i % 3 == 0) {
高进.add(arrayList.get(i));
} else if (i % 3 == 1) {
刀仔.add(arrayList.get(i));
} else {
星仔.add(arrayList.get(i));
}
}
//看牌
lookPoker("高进",高进,pokerBox);
lookPoker("刀仔",刀仔,pokerBox);
lookPoker("星仔",星仔,pokerBox);
lookPoker("底牌",底牌,pokerBox);
}
private static void lookPoker(String name, TreeSet<Integer> list,HashMap<ArrayList<Integer>,String> Box) {
System.out.println(name);
for (Integer integer : list) {
System.out.print(integer + " ");
}
System.out.println("\n");
}
}
Map和Collection的区别
- Map是双列的,Collection是单列的
- Map的键唯一,Collection的子体系Set是唯一的
- Map集合的数据结构针对键有效,跟值无关;Collection集合的数据结构是针对元素有效