概述
Collection
|--List
有序(存储顺序和取出顺序一致),可重复
|--Set
无序(存储顺序和取出顺序不一致),唯一
HashSet:它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。
注意:虽然Set集合的元素无序,但是,作为集合来说,它肯定有它自己的存储顺序,
而你的顺序恰好和它的存储顺序一致,这代表不了有序,你可以多存储一些数据,就能看到效果。
package cn.itcast_01;
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("hello");
set.add("java");
set.add("world");
set.add("java");
set.add("world");
for (String s : set) {
System.out.println(s);
}
}
}
HashSet的唯一性
问题:为什么存储字符串的时候,字符串内容相同的只存储了一个呢?
通过查看add方法的源码,我们知道这个方法底层依赖 两个方法:hashCode()和equals()。
步骤:
首先比较哈希值
如果相同,继续走,比较地址值或者走equals()
如果不同,就直接添加到集合中
按照方法的步骤来说:
先看hashCode()值是否相同
相同:继续走equals()方法
返回true: 说明元素重复,就不添加
返回false:说明元素不重复,就添加到集合
不同:就直接把元素添加到集合
如果类没有重写这两个方法,默认使用的Object()。一般来说不同相同。
而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
package cn.itcast_02;
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
for (String s : hs) {
System.out.println(s);
}
}
}
需求:存储自定义对象,并保证元素的唯一性
要求:如果两个对象的成员变量值都相同,则为同一个元素。
目前是不符合我的要求的:因为我们知道HashSet底层依赖的是hashCode()和equals()方法。
而这两个方法我们在学生类中没有重写,所以,默认使用的是Object类。
这个时候,他们的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。
package cn.itcast_02;
import java.util.HashSet;
public class HashSetDemo2 {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<Student>();
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("柳岩", 22);
Student s3 = new Student("王祖贤", 30);
Student s4 = new Student("林青霞", 27);
Student s5 = new Student("林青霞", 20);
Student s6 = new Student("范冰冰", 22);
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
hs.add(s5);
hs.add(s6);
for (Student s : hs) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
package cn.itcast_02;
/**
* @author Administrator
*
*/
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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 int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
HashSet的子类LinkedHashSet
LinkedHashSet:底层数据结构由哈希表和链表组成。
哈希表保证元素的唯一性。
链表保证元素有素。(存储和取出是一致)
package cn.itcast_04;
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet<String> hs = new LinkedHashSet<String>();
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
hs.add("java");
for (String s : hs) {
System.out.println(s);
}
}
}
TreeSet
TreeSet:能够对元素按照某种规则进行排序。
排序有两种方式
A:自然排序
B:比较器排序
TreeSet集合的特点:排序和唯一
通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法。
自然排序
package cn.itcast_05;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<Integer>();
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
ts.add(17);
ts.add(24);
ts.add(19);
ts.add(18);
ts.add(24);
for (Integer i : ts) {
System.out.println(i);
}
}
}
TreeSet存储自定义对象并保证排序和唯一。
A:你没有告诉我们怎么排序
自然排序,按照年龄从小到大排序
B:元素什么情况算唯一你也没告诉我
成员变量值都相同即为同一个元素
package cn.itcast_05;
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>();
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
package cn.itcast_05;
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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 int compareTo(Student s) {
int num = this.age - s.age;
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
}
比较器排序
需求:请按照姓名的长度排序
TreeSet集合保证元素排序和唯一性的原理
唯一性:是根据比较的返回是否是0来决定。
排序:
A:自然排序(元素具备比较性)
让元素所属的类实现自然排序接口 Comparable
B:比较器排序(集合具备比较性)
让集合的构造方法接收一个比较器接口的子类对象 Comparator
package cn.itcast_07;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getName().length() - s2.getName().length();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
练习题
键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
分析:
A:定义学生类
B:创建一个TreeSet集合
C:总分从高到底如何实现呢?
D:键盘录入5个学生信息
E:遍历TreeSet集合
package cn.itcast_08;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s2.getSum() - s1.getSum();
int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
: num4;
return num5;
}
});
System.out.println("学生信息录入开始");
for (int x = 1; x <= 5; x++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第" + x + "个学生的姓名:");
String name = sc.nextLine();
System.out.println("请输入第" + x + "个学生的语文成绩:");
String chineseString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的数学成绩:");
String mathString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的英语成绩:");
String englishString = sc.nextLine();
Student s = new Student();
s.setName(name);
s.setChinese(Integer.parseInt(chineseString));
s.setMath(Integer.parseInt(mathString));
s.setEnglish(Integer.parseInt(englishString));
ts.add(s);
}
System.out.println("学生信息录入完毕");
System.out.println("学习信息从高到低排序如下:");
System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
for (Student s : ts) {
System.out.println(s.getName() + "\t" + s.getChinese() + "\t"
+ s.getMath() + "\t" + s.getEnglish());
}
}
}
编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
分析:
A:创建随机数对象
B:创建一个HashSet集合
C:判断集合的长度是不是小于10
是:就创建一个随机数添加
否:不搭理它
D:遍历HashSet集合
package cn.itcast_08;
import java.util.HashSet;
import java.util.Random;
public class HashSetDemo {
public static void main(String[] args) {
Random r = new Random();
HashSet<Integer> ts = new HashSet<Integer>();
while (ts.size() < 10) {
int num = r.nextInt(20) + 1;
ts.add(num);
}
for (Integer i : ts) {
System.out.println(i);
}
}
}
终极练习题
模拟斗地主洗牌和发牌
分析:
A:创建一个牌盒
B:装牌
C:洗牌
D:发牌
E:看牌
思路:
A:创建一个HashMap集合
B:创建一个ArrayList集合
C:创建花色数组和点数数组
D:从0开始往HashMap里面存储编号,并存储对应的牌。同时往ArrayList里面存储编号即可。
E:洗牌(洗的是编号)
F:发牌(发的也是编号,为了保证编号是排序的,就创建TreeSet集合接收)
G:看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
package cn.itcast_04;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
public class PokerDemo {
public static void main(String[] args) {
HashMap<Integer, String> hm = new HashMap<Integer, String>();
ArrayList<Integer> array = new ArrayList<Integer>();
String[] colors = { "♠", "♥", "♣", "♦" };
String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q",
"K", "A", "2", };
int index = 0;
for (String number : numbers) {
for (String color : colors) {
String poker = color.concat(number);
hm.put(index, poker);
array.add(index);
index++;
}
}
hm.put(index, "小王");
array.add(index);
index++;
hm.put(index, "大王");
array.add(index);
Collections.shuffle(array);
TreeSet<Integer> fengQingYang = new TreeSet<Integer>();
TreeSet<Integer> linQingXia = new TreeSet<Integer>();
TreeSet<Integer> liuYi = new TreeSet<Integer>();
TreeSet<Integer> diPai = new TreeSet<Integer>();
for (int x = 0; x < array.size(); x++) {
if (x >= array.size() - 3) {
diPai.add(array.get(x));
} else if (x % 3 == 0) {
fengQingYang.add(array.get(x));
} else if (x % 3 == 1) {
linQingXia.add(array.get(x));
} else if (x % 3 == 2) {
liuYi.add(array.get(x));
}
}
lookPoker("风清扬", fengQingYang, hm);
lookPoker("林青霞", linQingXia, hm);
lookPoker("刘意", liuYi, hm);
lookPoker("底牌", diPai, hm);
}
public static void lookPoker(String name, TreeSet<Integer> ts,
HashMap<Integer, String> hm) {
System.out.print(name + "的牌是:");
for (Integer key : ts) {
String value = hm.get(key);
System.out.print(value + " ");
}
System.out.println();
}
}