Set;HashSet;LinkedHashSet;Set如何保证元素唯一;Map集合 (Java Day15)
一,Set
- 概述:他是单列集合的无序集合,他是所有无序集合的顶层接口。里面的所有功能就是无序集合的共性功能;但是他没有独有的功能,他的功能都是Collection里面的功能,使用的时候就是用Collection接口里面的功能
- 特点:
- 存取数据的顺序不一致【无序】
- 没有索引【无索引】
- 存放的元素要唯一,不可重复。【元素唯一】
- 常见的使用场景:用来进行去重。
-
Set的子类
-
HashSet:
- 概述:就是单纯体现无序集合的功能,没有自己特有的功能,体现无序集合的特点。
代码示例:
import java.util.HashSet;
import java.util.Iterator;
public class Demo_HashSet {
public static void main(String[] args) {
//创建HashSet集合
//构造方法
HashSet<String> set=new HashSet<>();
set.add("3");
set.add("6");
set.add("3");
set.add("2");
set.add("4");
System.out.println(set); //输出的顺序无序,元素不重复唯一,无索引,如果是数字的话是从小到大排序的 [2, 3, 4, 6]
set.remove("4");
System.out.println(set); //[2, 3, 6]
System.out.println(set.size()); //3
-
遍历
import java.util.HashSet;
import java.util.Iterator;
public class Demo_HashSet {
public static void main(String[] args) {
//创建HashSet集合
//构造方法
HashSet<String> set=new HashSet<>();
set.add("3");
set.add("6");
set.add("3");
set.add("2");
set.add("4");
System.out.println(set); //输出的顺序无序,元素不重复唯一,无索引,如果是数字的话是从小到大排序的 [2, 3, 4, 6]
set.remove("4");
System.out.println(set); //[2, 3, 6]
System.out.println(set.size()); //3 //第一种:数组法遍历
//集合转数组的时候不管集合是否给定具体的数据类型,都转变成Object类型的数组
//弊端 遍历之后需要强制转换
Object[] arr = set.toArray();
for (int i = 0; i < arr.length; i++) {
String s = (String) arr[i]; //集合已指定了泛型所以需要强转
System.out.println(s);
}
System.out.println("========");
//第二种:迭代器遍历
//迭代器对象 [Iterator ListIterator]
Iterator<String> it = set.iterator(); //it 为获取的迭代器对象
while (it.hasNext()) {
String ss = (String)it .next();// ss 为得到的内容,承载 next()得到的东西
System.out.println(ss);
}
System.out.println("========");
- 第三种:增强for遍历
- 概述:理解为是对迭代器遍历的格式得封装改进
- 格式:for(数据类型 变量名 :容器对象名){拿到元素的处理逻辑}
- 解释:
- for:关键字 代表了循环
- 数据类型:容器中存放的数据的数据类型
- 变量名:变量 用来存储每次循环遍历得到的内容值
- 容器对象名:要遍历的容器
- 好处:增强for单纯的进行遍历比较快捷简便
- 弊端:遍历的过程中不能够进行相应的增删操作
- 增强for的本质是什么?就是迭代器遍历
代码示例
import java.util.HashSet;
import java.util.Iterator;
public class Demo_HashSet {
public static void main(String[] args) {
//创建HashSet集合
//构造方法
HashSet<String> set=new HashSet<>();
set.add("3");
set.add("6");
set.add("3");
set.add("2");
set.add("4");
System.out.println(set); //输出的顺序无序,元素不重复唯一,无索引,如果是数字的话是从小到大排序的 [2, 3, 4, 6]
set.remove("4");
System.out.println(set); //[2, 3, 6]
System.out.println(set.size()); //3
//增强for遍历
for(String ss :set) {
System.out.println(ss);
}
}
}
- 总结:
- 数组法和迭代器以及增强for可以遍历所有的集合和数组。
- 普通for循环遍历能变遍历数组和有序集合【有索引的容器】
-
LinkedHashSet
- 概述:他是Set集合的一个实现类,同时也是HashSet的子类。他是无序集合中的有序集合,体现无序集合的另外两个特点【在存取顺序上LinkedHashSet是Set集合的叛徒】
- 特点:
- 存取有序【有序】
- 没有索引【无索引】
- 元素还是唯一不能重复【元素唯一不重复】
- 没有自己特有的功能,他的功能和HashSet一样都是Collection的功能
- 遍历也是和HashSet一样:数组法、迭代器、foreach
代码示例
import java.util.Iterator;
import java.util.LinkedHashSet;
public class Demo_LinkedHashSet {
public static void main(String[] args) {
LinkedHashSet<String>set = new LinkedHashSet<>();
set.add("春天");
set.add("夏天");
set.add("秋天");
set.add("冬天");
set.add("春天");
set.add("夏至");
set.add("冬至");
System.out.println(set);//[春天, 夏天, 秋天, 冬天, 夏至, 冬至] 有序排列
System.out.println("=======");
//第一种:数组遍历
Object[] arr = set.toArray();
for (int i = 0; i < arr.length; i++) {
String ss= (String)arr[i];
System.out.println(ss);
}
System.out.println("==========");
//第二种:迭代器遍历
Iterator<String>it = set.iterator();
while (it.hasNext()) {
String s = (String) it.next();
System.out.println(s);
}
System.out.println("==========");
//第三种:增强for遍历,(foreach)
for (String name : set) { // 用name 去接收
System.out.println(name);
}
}
}
- 练习
- 键盘录入一个字符串,输出其中的字符,相同字符只输出一次,要求保证原录入顺序
- 分析:
- 键盘录入一个字符串
- 操作的是字符串的字符 【想办法得到字符串的字符】
- 挨个把字符存到LinkedHashSet中
- 遍历输出或直接输出集合的内容
代码示例
import java.util.LinkedHashSet;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
//1、键盘录入一个字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
//2、操作的是字符串的字符 【想办法得到字符串的字符】
char[] cs = line.toCharArray();
//创建一个LinkedHashSet的对象
LinkedHashSet<Character> set = new LinkedHashSet<>();
for (char c : cs) {
//3、挨个把字符存到LinkedHashSet中
set.add(c);
}
//4、遍历输出或直接输出集合的内容
System.out.println(set);
}
}
二,Set如何保证元素唯一
- 重写hashCode
- 重写equals方法
- eclipse的操作:
使用快捷键,直接全部生成:alt + shift + s h
hashCode:是一个native方法,返回的是对象的内存地址,
重写这个方法可保证属性值相同的对象的哈希值是一样,不重写返回的哈希值不一样。
hashSet集合 就是根据对象的哈希值来存放数据的
- 原理:
- 调用被添加元素的hashCode(),和HashSet中已有元素的hashCode比较是否相同
1.1 、如果哈希值不相同,直接存储
1.2、 如果相同,需要调用equals方法比较是否相同
- 不相同,直接存储元素
- 相同,认为是同一元素.不存储
代码示例:
//定义一个Person类
public class Person {
private String name;
private int age;
private String sex;
//构造方法
public Person(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
//重写 toString 方法
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
//重写 hashCode 方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((sex == null) ? 0 : sex.hashCode());
return result;
}
//重写 equals 方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (sex == null) {
if (other.sex != null)
return false;
} else if (!sex.equals(other.sex))
return false;
return true;
}
}
//定义一个测试类
import java.util.HashSet;
public class Demo01 {
public static void main(String[] args) {
//创建HashSet
HashSet<Person> set = new HashSet<>();
//创建两个人物对象
Person p = new Person("Jack", 22, "男");
Person p1= new Person("Jack", 22, "男");
System.out.println(" P的hashcode:"+p.hashCode());
System.out.println(" P1的hashcode:"+p1.hashCode());
System.out.println(p.equals(p1));//比较属性值
System.out.println(p == p1); //比较地址值
//因为没有重写方法所以比较的是地址值
set.add(p1);
set.add(p);
System.out.println(set);
}
}
三,Map集合
- 概述:他是集合体系中的双列集合。每个位置上存放的是一对 [两个] 数据。而且这对数据具有映射关系。
- 映射关系:通过其一可以找到对应的另一个数据。比如:夫妻关系
- 键值对数据:就是map集合位置上存放的那对数据。
- 键:上面的数据有要求:唯一不可变 [在map集合的所有的键中]
- 值:可以有重复的。
- 在map集合中的键值对中key [键] 比较重要 [只有通过key可以找对应的值,不能通过值来找到对应的键]
- 比如:
- map特点:
- map集合是无序的
- 他的key值不能出现重复的值【key唯一】
- 他的值可以重复的
- 通过key可以精确的找到对应的值
-
Map集合的常用方法
- put(K k,V v):添加键值对数据到Map集合
- remove(K key):根据key删除该key的键值对数据
- clear():清空map集合
- get(K key):根据Key获取对应的value值
- containsKey(K k):判断集合的所有key中是否包含参数k
- containsValue(V v):判断集合的所有value值中是否包含参数v
代码示例:
import java.util.HashMap;
import java.util.Map;
public class Demo_Map {
public static void main(String[] args) {
//使用Map集合,map是一个接口 必须找他的实现类 使用hashMap
Map<String, String> map = new HashMap<>();
map.put("春天", "有花海");
map.put("夏天", "有大海");
map.put("秋天", "有果实");
System.out.println(map);//{夏天=有大海, 秋天=有果实, 春天=有花海} 结果无序
//为什么现实的不是地址值而是内容值,输出语句直接输出一个对象他是默认去调用toString方法,
//首先执行自己类中的toString,自己没有执行父类中的toString方法
map.put("春天", "春暖花开");
System.out.println(map);//put方法除了添加的功能还有修改键的值的行为,两个功能,添加/修改
map.remove("夏天");
System.out.println(map); //key删除了,值也跟着被删除了
String value=map.get("春天");
System.out.println(value); //春暖花开,只找现任,不找前任,前任被替换掉了。
System.out.println(map.containsKey("夏天"));//因为夏天已经被remove掉了 false
System.out.println(map.containsValue("春暖花开"));//true
System.out.println(map.keySet()); //[秋天, 春天]
System.out.println(map.entrySet()); // [秋天=有果实, 春天=春暖花开]
map.clear();
System.out.println(map); //{}
}
}
练习
- 键盘录入一个字符串,统计每个字符出现的次数 例如,录入
aaaabbccddd!@#@#$@#$%cc66ff
- 打印出来:a有4个,b有2个,c有4个,d有3个,!有1个,@有3个,$有2个,%有1个,6有2个,f有2个
分析:
代码示例
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Map_Test {
public static void main(String[] args) {
//1,键盘录入字符串
Scanner sc= new Scanner(System.in);
System.out.println("录入一个字符串:");
String line = sc.nextLine();
//2,获取所有字符串中有一个转换字符数组的方法
char[] cs = line.toCharArray();
//3,得到每一个字符和map集合的可以进行判断是否包含
//创建map集合
Map<Character, Integer> map= new HashMap<>();
for (char c : cs) {
//C就是每一个字符
// 进行和map集合的可以进行判断是否包含
if(map.containsKey(c)){
//3.1,包含更新字符的个数到集合
Integer value =map.get(c);
value++;
map.put(c, value);
}else {
//3.2,不包含添加字符到map集合
map.put(c, 1);
}
}
//4,输出map集合的内容
System.out.println(map);
}
}