Day17笔记
1 Set集合
- 概述:该集合是一个单列集合,是Collection集合的子接口。
- 特点:
(1)在java.util包中
(2)该类元素特点:
不可重复:集合中不可以存储相同的元素(去重)
没有索引:集合中没有一些操作元素索引的方法
无序:元素存储的顺序和取出的顺序不能保持一致 - HashSet集合有两个实现类:HashSet 和TreeSet
代码
package demos1_treeset;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo01 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("xyz");
set.add("abc");
set.add("123");
set.add("123");
set.add("早上好");
Object[] obs = set.toArray();
for (int i = 0; i < obs.length; i++) {
String str = (String)obs[i];
System.out.println(str);
}
String[] strs = new String[set.size()];
set.toArray(strs);
for (int i = 0; i < strs.length; i++) {
String str = strs[i];
System.out.println(str);
}
Iterator<String> it = set.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
for(String str:set){
System.out.println(str);
}
}
}
2 TreeSet集合
-
概述:TreeSet集合为Set集合的实现类。
-
构造方法:
TreeSet()
:创建一个集合对象,对元素自然排序
TreeSet(Comparator<? super E> comparator)
:使用Comparator比较器排序 -
特点:
(1)集合不可以存放重复的元素(去重)
(2)元素存入和取出的顺序不一致
(3)元素是按照二叉树的方式存储元素,按照大小排列元素
-
存储元素排序方式:
(1)如果是一个基本数据类型对应的包装类型的元素,按照数据的大小,从小到达排列
(2)如果是非中文字符串类型,则按照字母的顺序排序,从小到大排列
(3)如果是中文字符串则按照中文字符解码后的数字大小,从小到大排列(4)如果是自定义的类型对象,不能直接使用TreeSet集合存储
解决方案:实现Comparable接口,并重写接口中的compareTo方法 -
比较原理:
(1)Comparable接口: 是一个比较接口
(2)需要重写该接口的方法: public int compareTo(Object o)
(3)比较规则:
返回值为0 – 相等 如果相等,就去重
返回值为正 将添加的参数存储在二叉树的左子树(先取)
返回值为负 将添加的参数存储在二叉树的右子树(后取)
代码
package demos1_treeset;
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
//规则:
//如果方法的返回值为-1,默认添加的元素比较大,放在右子树
//如果方法的返回值为1,默认添加的元素比较小,放在左子树
//如果方法的返回值为0,默认将添加的元素视为重复的,就去重
//想法1:按照年龄从小到大排列
//this.age;//集合中已经存在的元素年龄(默认循环获取 二叉树中已经存在的元素)
// o.age;//即将要添加的元素的年龄
// return this.age-o.age;
//想法2:按照年龄从大到小排列
// return o.age-this.age;
//想法3:按照名字(String)的默认排序方式(自然排序)排列
//String类型已经默认实现Comparable接口,默认重写了compareto
// return this.name.compareTo(o.name);
//想法4:先按照年龄升序排列。如果年龄相同,再按照姓名默认排列
int result1 = this.age-o.age;
int result2 = this.name.compareTo(o.name);
return result1 == 0? result2 :result1;
}
}
3 Comparator比较器使用
- 概述:Comparator和Comparable一样都是用来比较数据的接口
- Comparable接口如果使用,需要被自定义类实现,且重写其中的compareTo方法
- Comparator接口如果使用,需要在创建TreeSet集合对象时传入一个该接口的实现类对象,且重写其中的compare方法
- 方法:compare(Object o1, Object o2):
将参数o1当作集合中存在的数据,将参数o2当作需要添加的数据 - 排序的方式和Comparable接口的排序方式相同
代码
package demos1_treeset;
import java.util.Comparator;
import java.util.TreeSet;
public class Demo04 {
public static void main(String[] args) {
Comparator<Stu> com = new Comparator<Stu>() {
@Override
public int compare(Stu o1, Stu o2) {
//方法中的两个参数:o1 o2
// o1:当做集合中已经存在的元素(底层默认循环获取)
// o2:当做即将要添加的元素即可
//按照年龄降序排列
// return o2.getAge()-o1.getAge();
//先按照年龄升序,年龄相同,再按照姓名自然排序
int result1 = o1.getAge()-o2.getAge();
int result2 = o1.getName().compareTo(o2.getName());
return result1 == 0?result2:result1;
}
};
TreeSet<Stu> tr = new TreeSet<>(com);
tr.add(new Stu("张三",23));
tr.add(new Stu("李四",24));
tr.add(new Stu("张三",24));
tr.add(new Stu("王五",25));
tr.add(new Stu("赵六",26));
tr.add(new Stu("赵六",26));
System.out.println(tr);
}
}
3.1 案例
使用TreeSet集合存储多个学生信息,按照学生的总成绩升序排列
如果总成绩相同,则按照语文成绩升序排列
如果语文成绩也相同,则按照名字的自然排序升序排列
学生类:
属性:姓名 语文成绩 数学成绩
代码
package demos1_treeset;
import java.util.Comparator;
import java.util.TreeSet;
public class Demo05 {
// 使用TreeSet集合存储多个学生信息,按照学生的总成绩升序排列
// 如果总成绩相同,则按照语文成绩升序排列
// 如果语文成绩也相同,则按照名字的自然排序升序排列
public static void main(String[] args) {
Comparator<Student> com = new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//按照从成绩升序排列
int result1 = o1.getSum() - o2.getSum();
//按照语文成绩升序排列
int result2 = o1.getChinese()-o2.getChinese();
//按照名字自然排序
int result3 = o1.getName().compareTo(o2.getName());
//先按照总成绩,总成绩相同,再按照语文成绩,语文成绩相同,按照名字排序
return result1 == 0 ? (result2 == 0 ? result3 : result2) : result1;
}
};
TreeSet<Student> tr = new TreeSet<>(com);
tr.add(new Student("张三",90,100));
tr.add(new Student("李四",100,90));
tr.add(new Student("王五",90,100));
tr.add(new Student("赵六",80,110));
tr.add(new Student("赵六",100,110));
System.out.println(tr);
}
}
4 HashSet集合
- 概念:属于set集合的实现类
- 特点:
(1)无序,没有索引,不可重复
(2)该集合没有特殊的方法,可以使用单列接口中定义的方法
(3)该集合存储元素的方式,底层是根据哈希表来进行存储的
4.1 哈希值简介
- 是JDK根据对象的地址算出来的int类型的数值
- 如何获取哈希值
Object类中的public int hashCode():返回对象的哈希码值 - 哈希值的特点
(1)同一个对象多次调用hashCode()方法返回的哈希值是相同的
(2)默认情况下,不同对象的哈希值不同,因为默认根据对象的地址值来获取
而重写hashCode()方法,可以实现让不同对象的哈希值相同 - 特点总结:
(1)两个对象的哈希值不同,肯定不是同一个对象
(2)如果两个对象的哈希值一样,可能不是同一个对象
4.2 哈希表图示
-
哈希表:底层就是使用 数组 + 链表 结合产生的。
JDK1.7版本哈希表的图示
使用数组和链表组成
JDK1.8之后对哈希表的优化
如果某一个链表上的元素个数>=8 会将当前链表转为一个红黑树(特殊二叉树)
目的:降低链表的长度(让数据查询起来快速一点)如果链表上的元素被减少到6个,会再次将该红黑树转为普通的链表
4.3 HashSet集合存储元素原理总结
- 要存储一个元素x,先获取x的哈希值,在将该哈希值进行处理,处理之后,获取一个 存储的位置
- 判断该位置是否有元素,如果该位置没有元素(就代表x的哈希值是唯一的),直接存入。
- 如果该位置有元素(集合中有元素的哈希值和x相同),就再次调用equals(重写)方 法比较这些元素的属性值
- 如果属性值也相同,就直接去重
- 如果属性值不相同,就存入到集合
代码
package demos2_hashset;
import java.util.HashSet;
public class Demo01 {
public static void main(String[] args) {
//默认:添加元素之前,获取当前元素的哈希值,如果哈希值不同,就直接存入(存入到指定的位置)
//对开发而言:如果两个对象的属性值相同,就要实现去重
//元素去重原理的总结:
/**
* 1、要存储一个元素x,先获取x的哈希值,在将该哈希值进行处理,处理之后,获取一个存储的位置
* 2、判断该位置是否有元素,如果该位置没有元素,直接存入。(如果集合中没有元素的哈希值和x相同)
* 3、如果该位置有元素(集合中有元素的哈希值和x相同),就再次调用equals(重写)方法比较哈希值相同的元素属性值
* 4、如果属性值也相同,就直接去重
* 5、如果属性值不相同,就存入到集合
*/
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三",23));//1 hashcode
hs.add(new Person("李四",24));//2 hashcode
hs.add(new Person("王五",25));//3 hashcode
hs.add(new Person("张三",23));//4 hashcode 4 equals
hs.add(new Person("李四",24));//5 hashcode 5 eauals
System.out.println(hs);
}
private static void test02() {
HashSet<String> hs = new HashSet<>();
hs.add("xyz");
hs.add("xyz");
hs.add("你好");
hs.add("abc");
System.out.println(hs);
}
private static void test01() {
HashSet<Integer> hs = new HashSet<>();
hs.add(555);
hs.add(333);
hs.add(444);
hs.add(666);
hs.add(111);
System.out.println(hs);
}
}
4.4 LinkedHashSet集合
- 概述:是一个单列集合,是HashSet集合的子类
- 特点:
(1)没有特殊的方法,可以使用父类中继承的方法
(2)元素特点:
无索引
不可重复:和父类去重的原理一样
有序:可以保证元素存入的和取出的顺序
原因:每一个元素都会记录下一个存入元素的地址,在取出元素的时候,根据寻找地址的方式来寻找下一个元素。
4.5 单列集合特点总结:
- Collection接口:单列集合的顶层接口
只要是一个单列集合都是该接口的实现类或者子接口
该接口中定义的方法,任何一个单列集合都可以使用 - List:子接口
特点:元素有序 有索引 可以重复存储
有自己的特殊方法:通过索引操作元素 - ArrayList:
List接口的实现类,底层数组实现,增删慢 查询块 - LinkedList:
List接口的实现类,底层使用链表实现,增删快,查询慢 - Set:子接口
特点:元素无序 没有索引 不可存储重复的元素(默认去重)
没有自己特殊方法操作元素 - TreeSet:
Set接口的实现类,底层使用二叉树实现
无序去重:
(1)元素实现Comparable接口,重写compareTo方法进行元素排序
(2)给集合传入一个比较器对象Compartor接口的实现类对象,重写compare - HashSet:
Set接口的实现类 底层使用哈希表存储(数组+链表)
无序方式:
根据要存储元素的哈希值来决定存在哪一个位置
去重方式:
默认比较元素的哈希值和属性值 - LinkedHashSet:
HashSet子类,特点:有序 去重 没有索引
重点掌握:
1、每一个集合的特点
2、集合中常用的方法
3、集合的遍历方式
5 Map集合
- 概念:
现实生活中,我们常会看到这样的一种关系:IP地址与主机名,身份证号与个人, 用户名与密码,等等。这种一一对应的关系,就叫做映射。Java提供了专门的集合类用 来存放这种对象关系的数据,这个集合就是即Map接口。 - 特点:
(1)该接口是双列集合顶层接口,不能直接创建对象
(2)该类在java.util.包中,使用时需要导包
(3)该集合中的每个元素是由一对数据组成,即:键值对
键值对是由 键(key)和值(value)组成
(4)键值对中的键是唯一的(不可重复),无序的
值是可以不唯一的,一般是通过键来操作值
5.1Map集合中常用的方法
函数名 | 解释 |
---|---|
put(K key, V value) | 往集合中添加元素 |
remove(Object key) | 根据制定的key删除对应的键值对 |
remove(Object key, Object value) | 根据键和值删除对应键值对 |
clear() | 清空集合 |
containsKey(Object key) | 判断集合中是否包含key |
containsValue(Object value) | 判断集合中是否包含value |
get(Object key) | 根据key值获取对应的value值 |
isEmpty() | 判断集合是否为空 |
replace(K key, V value) | 替换key对应的value值 |
size() | 获取集合中键值对的对数 |
注意:
对于put函数来说
如果key值是第一次出现,就是添加
如果key值第二次出现,就是替换
代码
package demos3_map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class Demo01 {
public static void main(String[] args) {
//创建一个双列集合对象
Map<Integer,String> map = new HashMap<>();
//在集合中添加键值对(映射关系的数据)
map.put(666,"北京");
map.put(111,"上海");
map.put(333,"广州");
map.put(444,"深圳");
System.out.println(map);
//根据key删除对应的键值对
map.remove(111);
//根据指定的键值对,删除对应的数据
map.remove(666,"北京");
System.out.println(map);
//清空集合 键值对一并删除
// map.clear();
// System.out.println(map);
//判断集合中是否包含该key
System.out.println(map.containsKey(444));
//判断集合中是否包含该value
System.out.println(map.containsValue("深圳"));
//根据key获取对应的value
System.out.println(map.get(333));
//不能反过来根据value获取key
System.out.println(map.get("广州"));//null
//判断集合是否是空
System.out.println(map.isEmpty());
//替换key对应的value
// map.replace(444,"北京");
//返回集合中键值对的对数
System.out.println(map.size());
}
}
6 Map集合的遍历
6.1 第一种遍历方式:通过key来获取值的方式【重点掌握】
- 先获取集合中的key到一个set集合中,遍历set集合获取每一个key,再通过key单独 拿到对应的value即可。
- 获取集合中的key到set集合中:keySet()
- 遍历set集合
- 通过get方法获取key值对应的value值
- 图示:
代码
package demos3_map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Demo02 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(111,"北京");
map.put(222,"上海");
map.put(333,"广州");
map.put(444,"深圳");
//将map中的每一个key获取到set集合中
Set<Integer> set = map.keySet();
//遍历set集合获取每一个key
Iterator<Integer> it = set.iterator();
while(it.hasNext()){
Integer key = it.next();
String value = map.get(key);
System.out.println("key:" + key + "...value:" + value);
}
}
private static void test(Map<Integer, String> map, Set<Integer> set) {
for(Integer key: set){
//根据获取的key,再获取对应的value
String value = map.get(key);
System.out.println("key:" + key + "...value:" + value);
}
}
}
6.2 Map集合的第二种遍历方式:通过键值对对象获取键和值
- 先获取map集合中的键值对,获取的时候将键值对封装为一个个对象放到set集合中, 再遍历set集合获取每一对键值对对象,单独获取这一对数据的key和value.
- 获取键值对到set集合中:entrySet()
获取的时候会将键值对,封装为一个个Entry对象,进行保存
所以集合Set中保存的是一个个键值对对象(Entry对象)
Entry:是Map集合中的一个内部接口
提供了两个方法:getKey()获取key
getValue()获取value - 遍历set集合,获取每一个entry对象
- 单独获取键值对对象中的key和value值:
获取key值:entry接口中的getKey()
获取value值:接口中的getValue()
代码
package demos3_map;
import java.util.*;
import java.util.Map.Entry;
public class Demo03 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(111,"北京");
map.put(222,"上海");
map.put(333,"广州");
map.put(444,"深圳");
//将键值对封装为一个个对象,保存在set集合中
Set<Entry<Integer, String>> set = map.entrySet();
//遍历set集合,获取每一个键值对对象
Iterator<Entry<Integer, String>> it = set.iterator();
while(it.hasNext()){
Entry<Integer, String> en = it.next();
Integer key = en.getKey();
String value = en.getValue();
System.out.println("key:" + key + "...value:" + value);
}
}
private static void test(Set<Entry<Integer, String>> set) {
for(Entry<Integer,String> en: set){
//单独获取该对对象中的key和value
Integer key = en.getKey();
String value = en.getValue();
System.out.println("key:" + key + "...value:" + value);
}
}
}