1. Set
1.1 Set集合的特点
- 不包含重复元素的集合
- 没有带索引的方法,所以不能使用普通for循环遍历
- 是一个接口,不能直接实例化
package Java18.Set;
//HashSet<E>对集合的迭代次序不作任何保证
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("world");
set.add("java");
//不包含重复元素
set.add("world");
//遍历
for(String s : set){
System.out.println(s);
}
}
1.2 哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
- public int hashCode():返回对象的哈希码值
对象的哈希值特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同。
package Java18.Set;
public class hashdemo {
public static void main(String[] args) {
//创建学生对象
Student s = new Student("lyy",24);
//同一个对象多次调用hashCode()方法返回的哈希值相同
System.out.println(s.hashCode());//356573597
System.out.println(s.hashCode());
//默认情况下不同对象的哈希值是不一样的
//通过方法重写可以实现不同对象的哈希值是相同的
Student s2 = new Student("lyy",24);
System.out.println(s2.hashCode());//1735600054
System.out.println("----------");
System.out.println("hello".hashCode());//99162322
System.out.println("world".hashCode());//113318802
System.out.println("world".hashCode());//113318802
System.out.println("-------------------------");
System.out.println("重地".hashCode());//1179395
System.out.println("通话".hashCode());//1179395
}
}
1.3 HashSet集合概述和特点
HashSet集合特点
- 底层数据结构为哈希表
- 对集合的迭代次序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
package Java18.Set;
import java.util.HashSet;
import java.util.Iterator;
public class demo2 {
public static void main(String[] args) {
HashSet<String> hash = new HashSet<String>();
hash.add("l");
hash.add("w");
hash.add("d");
hash.add("q");
//遍历
Iterator<String> it = hash.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("------------");
for (String s : hash){
System.out.println(s);
}
}
}
1.3 HashSet集合保证元素唯一性源码分析
HashSet<String> hash = new HashSet<String>();
hash.add("l");
hash.add("w");
hash.add("d");
hash.add("q");
-----------------------------------------------
//调用add方法将元素存储进hashSet,add方法中调用put方法,其中e为传入的元素
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//元素参数对于key,其中hash(key)表示元素的哈希值,然后在put方法中调用putVal方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
//利用hashCode()方法获取key的哈希值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//hash和元素的hashCode()方法相关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果哈希表未初始化,就对其进行初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//根据对象的hash值计算对象的存储位置,如果该位置没有元素就存储元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
/*
存入的元素和以前的元素比较哈希值
如果哈希值不同,会继续向下执行把元素添加到集合
如果哈希值相同,会调用对象的equals方法来进行比较
如果返回false,会继续向下执行把元素添加到集合
如果返回true,说明元素重复不存储
*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
1.4 HashSet 集合存储学生对象并遍历
学生类,在该类中重写hashCode和equal方法,使得成员变量相同的学生认为是同一个变量
package Java18.Set;
public class Student {
private String name;
private int age;
public Student(){
}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
//重写hashCode和equal方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
package Java18.Set;
import java.util.HashSet;
import java.util.Iterator;
public class HashsetDemo {
public static void main(String[] args) {
HashSet<Student> ha = new HashSet<Student>();
//创建学生对象
Student s1 = new Student("lyy",23);
Student s2 = new Student("lyq",24);
Student s3 = new Student("sss",43);
Student s4 = new Student("lyy",23);
//集合中添加元素
ha.add(s1);
ha.add(s2);
ha.add(s3);
ha.add(s4);
//遍历
Iterator<Student> it = ha.iterator();
while(it.hasNext()){
Student s = it.next();
System.out.println(s.getName()+","+s.getAge());
}
System.out.println("---------------");
for(Student s : ha){
System.out.println(s.getName()+","+s.getAge());
}
}
}
1.5 LinkedHashSet 集合概述和特点
- 哈希表和链表实现了Set接口,具有可预测的迭代次序。
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复的元素
这种实现不同于HashSet,它维持于所有条目的运行双向链表。
练习:存储字符串并遍历
package Java18.Set;
import java.util.Iterator;
import java.util.LinkedHashSet;
public class LinkedHashSetdemo {
public static void main(String[] args) {
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
//添加元素
lhs.add("lll");
lhs.add("yyy");
lhs.add("qqq");
lhs.add("yyy");
//遍历
//实现set接口 可以调用Iterator迭代器
Iterator<String> it = lhs.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("---------------");
//增强for
for(String s : lhs){
System.out.println(s);
}
}
}
1.6 TreeSet 集合概述和特点
TreeSet 集合的特点
- 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体的排序方式取决于构造方法
TreeSet ():根据元素的自然顺序进行排序
TreeSet (Comparator , comparator):根据指定的比较器进行排序 - 没有索引的方法,不能使用普通for循环遍历,可以使用增强for和迭代器进行遍历
联系:存储整数并遍历
package Java18.Set;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetdemo {
public static void main(String[] args) {
TreeSet<Integer> Tr = new TreeSet<Integer>();
//集合添加元素
Tr.add(1);
Tr.add(2);
Tr.add(3);
Tr.add(7);
Tr.add(5);
//遍历
//迭代器Iterator方法
Iterator<Integer> it = Tr.iterator();
while(it.hasNext()){
int i = it.next();
System.out.println(i);
}
System.out.println("---------------");
//增强for
for(int i : Tr){
System.out.println(i);
}
}
}
输出结果会按照整数数据进行排序输出
1.7 自然排序 Comparable的使用
- 存储学生对象并遍历,创建TreeSet 集合使用无参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
学生类及CompareTo方法的重写
package Java18.Set.Treeset;
public class Student implements Comparable<Student>{
private int age;
private String name;
public Student(){
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
//重写Comparable里面的方法
@Override
public int compareTo(Student s) {
// return 0;
// return 1;
// return -1;
//按照年龄从小到大排序
int num = this.age - s.age;
//年龄相同时,按照姓名的字母排序
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
学生类调用及排序
package Java18.Set.Treeset;
import java.util.TreeSet;
public class Treesetdemo2 {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>();
//创建学生对象
Student s1 = new Student("lll",22);
Student s2 = new Student("sss",23);
Student s3 = new Student("eee",22);
Student s4 = new Student("qqq",19);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
//遍历
for(Student s : ts){
System.out.println(s.getName()+","+s.getAge());
}
}
}
1.7 比较器Comparatot的使用
- 存储学生对象并遍历,创建TreeSet 集合使用无参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
学生类
package Java18.Set.TreeSet2;
public class Student {
private String name;
private int age;
public Student(){
}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
测试类
package Java18.Set.TreeSet2;
import java.util.Comparator;
import java.util.TreeSet;
public class Treesetdemo1 {
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.getAge() - s2.getAge();
int num2 = num==0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
//创建学生对象
Student s1 = new Student("lll", 22);
Student s2 = new Student("sss", 23);
Student s3 = new Student("eee", 22);
Student s4 = new Student("qqq", 19);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
//遍历
for (Student s : ts) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写Comparator(To 1 ,T o2)方法
1.8 案例:学生成绩排序
两种方法:
- 自然排序Comparable的使用,学生类中重写CompareTo方法
package Java18.Set.Treeset3;
public class Student implements Comparable<Student> {
private String name;
private int ma;
private int ch;
public Student(){
}
public Student(String name,int ma,int ch){
this.name = name;
this.ma = ma;
this.ch = ch;
}
public void setName(String name){
this.name = name;
}
public void setMa(int ma){
this.ma = ma;
}
public void setCh(int ch){
this.ch = ch;
}
public String getName(){
return name;
}
public int getMa(){
return ma;
}
public int getCh(){
return ch;
}
@Override
public int compareTo(Student s) {
//先按总成绩排序
int num = s.ma+s.ch-this.ma-this.ch;
//若成绩相同,先比较语文成绩,若相同则按名字排序
int num2 = num==0?s.ch-this.ch:num;
int num3 = num2 == 0?s.name.compareTo(this.name):num2;
return num3;
}
}
package Java18.Set.Treeset3;
import java.util.TreeSet;
public class demo {
public static void main(String[] args) {
//创建集合
TreeSet<Student> ts = new TreeSet<Student>();
//创建学生对象
Student s1 = new Student("lyy",93,96);
Student s2 = new Student("lyq",95,97);
Student s3 = new Student("dqd",90,93);
Student s4 = new Student("hsy",85,91);
Student s5 = new Student("dst",91,85);
//集合添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历并按成绩排序成绩相同则按姓名
for(Student s : ts){
System.out.println(s.getName()+","+s.getCh()+","+s.getMa());
}
}
}
- 比较器Comparatot的使用
学生类
package Java18.Set.Treeset3;
//比较器的使用
public class Student2 {
private String name;
private int ma;
private int ch;
public Student2(){
}
public Student2(String name, int ma, int ch) {
this.name = name;
this.ma = ma;
this.ch = ch;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMa() {
return ma;
}
public void setMa(int ma) {
this.ma = ma;
}
public int getCh() {
return ch;
}
public void setCh(int ch) {
this.ch = ch;
}
//定义一个求总分的方法
public int getSum(){
return this.ch+this.ma;
}
}
测试类,使用局部内部类重写compare方法
package Java18.Set.Treeset3;
//比较器的使用
import java.util.Comparator;
import java.util.TreeSet;
public class demo2 {
public static void main(String[] args) {
//创建集合对象,调用局部内部类
TreeSet<Student2> ts = new TreeSet<Student2>(new Comparator<Student2>() {
@Override
public int compare(Student2 s1, Student2 s2) {
//比较总分
// int num = s2.getCh()+s2.getMa()-s1.getCh()-s1.getMa();
int num = s2.getSum()-s1.getSum();
//若总分相同,比较语文成绩,语文成绩相同比较名字进行排序
int num2 = num==0?s1.getCh()-s2.getCh():num;
int num3 = num2==0?s1.getName().compareTo(s2.getName()):num;
return num3;
}
});
//创建学生对象
Student2 s1 = new Student2("lyy",93,96);
Student2 s2 = new Student2("lyq",95,97);
Student2 s3 = new Student2("dqd",90,93);
Student2 s4 = new Student2("hsy",85,91);
Student2 s5 = new Student2("dst",91,85);
//集合添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历并按成绩排序成绩相同则按姓名
for(Student2 s : ts){
System.out.println(s.getName()+","+s.getCh()+","+s.getMa()+","+s.getSum());
}
}
}
//获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
package Java18.Set.TreeSet4;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class demo {
public static void main(String[] args) {
//创建对象
Set<Integer> s = new HashSet<Integer>();
//创建随机数对象
Random r = new Random();
while(s.size()<=10){
//产生一个随机数添加到集合
int number = r.nextInt(20)+1;
s.add(number);
}
for(Integer i:s){
System.out.println(i);
}
}
}