笔记13
课程内容
1、Set集合
2、HashSet存储自定义元素保证唯一性的原理
3、Map双列集合的顶层接口
4、HashMap哈希存储
5、Collections工具类
一、Set集合
1、set集合是Collection下的一个子接口
2、特点:
无序:没有任何前后的区别,存入的顺序和取出的顺序是不一样的,所有的元素在集合中没有位置上的概念
没有索引
不能重复,没有位置上的区别,相同的元素没有任何区分,所有不能重复
3、Set的实现类:
HashSet:使用哈希表的存储方式来存储元素
4、存储特点:
相同的元素不能存入到set集合中
集合本身不保证顺序的,存入的顺序和取出的顺序是不一样的
package com.ujiuye.demos;
import java.util.HashSet;
import java.util.Set;
public class Demo_1 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("赵丽颖");
set.add("赵丽颖");
set.add("迪丽热巴");
set.add("迪丽热巴");
set.add("杨幂");
set.add("杨幂");
set.add("陈乔恩");
set.add("陈乔恩");
set.add("王丽坤");
System.out.println(set);
Set<String> set1 = new HashSet<>();
set1.add("a");
set1.add("b");
set1.add("c");
System.out.println(set1);
}
}
Set集合的遍历
1、没有特有的方法,只能使用Collection接口中定义的方法,只能使用Collection的遍历方式
2、第一种:转成数组toArray(),不带泛型的数组,得到的是Object类型的数组
3、第二种: 转成数组 T[] toArray(T[] t),带泛型的数组,得到的是T类型的数组
(1)自己创建的数组大小,小于集合元素的个数
在方法中,就会创建一个新的数组,用来存储集合中的元素,将数组返回
(2)自己创建的数组大小,等于集合元素的个数
在方法中,就不会创建一个新的数组,使用集合中的元素将传入的数组进行填充,将原数组进行返回
(3)自己创建的数组大小,大于集合元素的个数
在方法中,不会创建一个新的数组,直接将集合中的元素填充到数组中的前面的位置,后面的位置用默认值填充
4、第三种:迭代器
5、第四种:增强for循环(foreach)
格式:for(元素的数据类型 元素名称 :要遍历的集合或者数组){
使用元素名称代表当前访问的元素;
}
说明:
元素的数据类型:指的是要遍历集合中元素的数据类型
元素名称:元素名称虽然是固定的,但是随着循环的执行,每次代表的元素是不同的
要遍历的集合:可以是数组也可以是集合
本质:
底层迭代器,只不过使用这种格式更为简单
注意事项:使用增强for,没有拿到元素的索引,无法修改集合或者数组中的元素值
底层迭代器,所在在遍历集合的时候,使用集合对象添加元素,也会发生并发修改异常
package com.ujiuye.demos;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class Demo_2 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("赵丽颖");
set.add("赵丽颖");
set.add("迪丽热巴");
set.add("迪丽热巴");
set.add("杨幂");
set.add("杨幂");
set.add("陈乔恩");
set.add("陈乔恩");
set.add("王丽坤");
System.out.println(set);
for(String str : set) {
System.out.println(str);
}
int[] arr = {1,2,3,4};
for(int i : arr) {
System.out.println(i);
}
}
private static void bianLi_3(Set<String> set) {
Iterator<String> it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
private static void bianLi_2(Set<String> set) {
String[] strs1 = new String[3];
String[] array1 = set.toArray(strs1);
System.out.println(strs1 == array1);
System.out.println(Arrays.toString(array1));
System.out.println("=========自己创建的数组大小,等集合元素的个数========================");
String[] strs2 = new String[set.size()];
String[] array2 = set.toArray(strs2);
System.out.println(strs2 == array2);
System.out.println(Arrays.toString(array2));
System.out.println("=========自己创建的数组大小,大于集合元素的个数========================");
String[] strs3 = new String[set.size() + 3];
String[] array3 = set.toArray(strs3);
System.out.println(strs3 == array3);
System.out.println(Arrays.toString(strs3));
}
private static void bianLi_1(Set<String> set) {
Object[] objs = set.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i]);
}
}
}
二、HashSet保证元素唯一性的原理
HashSet存储jdk提供的元素的类型
HashSet存储jdk提供的元素的类型,发现直接保证了元素的唯一性,值相同的元素都去掉了
HashSet存储自定义类型元素
1、HashSet存储自定义类型元素发现并没有保证唯一性
2、实验:猜测没有重写equals方法,没有重写之前比较的地址值,如果地址值一样了就不存储,不一样就存储,但是
重写后equals方法比较的是属性值,发现并没有调用,没有去重成功
3、猜测Hashset集合是哈希实现,有没有可能跟HashCode方法有关,重写HashCode方法
在重写hashcode方法之前,调用的Object中的hashcode方法,不同的对象(对象的地址值不同)生成的hashcode值就是不同的。
重写hashcode方法之后,让Person中hashcode方法生成的值都为0(实现的效果,不同的对象对象的地址值不同的时候)生成的hashCode值一样的,发现确实hashCode方法执行了,equals方法也执行了。
4、发现了,HashSet存储自定义类型元素的时候,确实要调用自定义元素类型中的hashCode方法,根据hashCode方法的值判断是否将自定义元素存入到集合中。发现不同的对象(地址值不同)调用Object类中的hashCode方法生成的是不同的的hashcode值,就直接存储,不能实现去重,equals方法也不调用
重写hashcode方法之后,让Person中hashcode方法生成的值都为0,发现调用equals方法,根据equasl方法比较的结果判断是否添加重复的元素
HashCode方法
1、Object类型中的方法
2、根据对象,生成一个整数,就是哈希吗值,生成数字的方法的就是hashCode()
3、生成数字的原则:
(1)同一个对象,多次调用hashCode()方法【必须】返回的是相同的数字(程序多次运行不要求hashCode码值一致)
(2)使用equals(Object) 方法判断相同的两个对象【必须】返回相同的整数(equals是Object类型中的,比较的是地址值)
(3)使用equals(Object) 方法判断不相同的两个对象【尽量】生成不相同的整数(不做强制要求)
(4)Object类型中的HashCode方法,确实会根据不同的对象生成不同的整数。
package com.ujiuye.demos;
public class Demo_4 {
public static void main(String[] args) {
Object obj = new Object();
Object obj2 = new Object();
System.out.println(obj.hashCode());
System.out.println(obj.hashCode());
System.out.println(obj.hashCode());
System.out.println(obj.hashCode());
System.out.println(obj2.hashCode());
Object obj1 = obj;
System.out.println(obj1.hashCode());
System.out.println(obj2.hashCode());
}
}
HashSet保证唯一性原理的总结
1、HashSet 集合存储一个obj对象的时候,首先计算obj对象的hashcode值
2、在集合中的所有的元素的哈希值,都和obj的hashcode值,说明集合中不存在Obj,可以直接将obj存储到集合中
3、在集合中有若干个元素的哈希值,和obj的哈希值相同,并不能说明obj已经存在于集合中,需要使用equals方法判断obj时候和那些与自己相同的哈希值的元素是否相等
4、如果在equals方法和obj元素比较之后,发现都不相等,那么就说明obj不存在与这个集合,可以将obj存储到hashset中
5、如果在equals方法和obj元素比较之后,发现相等,obj已经存在于集合中,obj就不存储了
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200806192250739.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTgwNDUzNw==,size_16,color_FFFFFF,t_70)
保证唯一性的操作
1、重写HashCode()方法
根据属性值,生成哈希值
不同的属性值,尽量生成不同的整数
相同的属性值,一定会生成相同的整数
2、重写equals方法
比较的就是属性值
3、操作
alt+shift + s
h
生成hashcode方法和equals方法
package com.ujiuye.demos;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
System.out.println("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) {
System.out.println("equals方法执行了");
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;
return true;
}
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 + "]";
}
}
package com.ujiuye.demos;
import java.util.HashSet;
public class Demo_3 {
public static void main(String[] args) {
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三丰", 90));
hs.add(new Person("张三丰", 90));
hs.add(new Person("张无忌", 30));
hs.add(new Person("赵敏", 20));
hs.add(new Person("赵敏", 20));
hs.add(new Person("灭绝师太", 190));
System.out.println(hs);
}
}
LinkedHashSet
1、是HashSet的一个子类,和HashSet保证元素唯一性的原理相同
2、将来每个元素在存储的时候,都记录了前后元素的地址
3、效果:
可以根据存储元素的顺序,将元素取出
4、应用:
既需要保证元素的唯一,又需要保证原来的顺序,就可以考虑LinedHashSet
package com.ujiuye.demos;
import java.util.LinkedHashSet;
public class Demo_5 {
public static void main(String[] args) {
LinkedHashSet<String> lhs = new LinkedHashSet<String>() ;
lhs.add("java");
lhs.add("java");
lhs.add("C");
lhs.add("C++");
lhs.add("C++");
lhs.add("C#");
System.out.println(lhs);
LinkedHashSet<Person> lhs1 = new LinkedHashSet<>() ;
lhs1.add(new Person("王思聪",31));
lhs1.add(new Person("王健林",31));
lhs1.add(new Person("郭美美",31));
lhs1.add(new Person("郭美美",31));
lhs1.add(new Person("凤姐",31));
lhs1.add(new Person("凤姐",31));
System.out.println(lhs1);
}
}
三、Map
1、是双列集合的顶层接口
2、Map:地图
3、Map:映射,描述的就是将一个数据(key)到另一个数据(value)的映射关系(对应关系)
一个数据(key):容易记忆的,有规律的,简单的数据
另一个数据(value):没有规律,不容易记忆的,复杂的数据
大多数都是通过key操作value
4、映射:对应关系
函数:y = x * x 特点:x是不会重复的 key
平方 y会重复的 value
x -----> y
1 1
2 4
3 9
4 16
5、java中map就是使用穷举罗列的方式描述映射关系
6、map的特点
key(键) 是唯一的,不能重复的
value(值)不是唯一的,可以重复
7、Map和Collection的区别
Map是双列集合,Collection单列集合
Map的键是唯一的,Collection中set子接口中的元素是唯一的
Map中大多数方法都是操作键的,Collection中set子接口中的操作对元素有效
Map集合中的方法
1、增加键值对(向map集合中添加数据)
put(K key,V value)
2、删除方法
根据给定的值,删除对应的键值对:remove(k key)
clear(); 清空
3、获取方法
get(k key)获取键所对应的的value值
size()返回map集合中所有的键值对的个数
4、判断方法
containsKey(Object key) 判断集合中是否包含key这个键
containsValue(Object value) 判断集合中是否包含value这个值
5、修改方法
根据给定的键,修改对应的值:put(k key,v value)
如果在集合中已经存在key这个键,那么使用put方法,就是修改这个键对应的value值
如果在集合中不存在key这个键,那么使用put方法,就是向map集合重添加数据
package com.ujiuye.demos;
import java.util.HashMap;
import java.util.Map;
public class Demo_6 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("瞎子", "李青");
map.put("瞎子", "李青");
map.put("盲僧", "李青");
map.put("托儿索", "亚索");
map.put("快乐风男", "亚索");
map.put("望远烬", "烬");
map.put("望远烬", "烬");
System.out.println(map);
map.remove("望远烬");
System.out.println(map);
System.out.println(map.size());
System.out.println(map.get("盲僧"));
System.out.println(map.containsKey("李青"));
System.out.println(map.containsKey("盲僧"));
System.out.println(map.containsValue("李青"));
map.put("奥斑马","卢锡安");
System.out.println(map);
map.put("瞎子","李青2号");
System.out.println(map);
}
}
Map集合的第一种遍历思路(熟练掌握)
1、获取Map集合中所有的键,放到一个set集合中,遍历set集合,获取每一个键,根据键再来获取对应的值【根据键获取对应的值】
2、获取Map集合中所有的键
keySet()
3、遍历Set集合拿到map集合中所有的键了
遍历Set集合:
增强for
迭代器
4、拿到每个键之后,通过get(K key)获取键对应的值
5、特点;
获取了所有键的set集合后,要依赖于原来的map集合
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200806192334213.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTgwNDUzNw==,size_16,color_FFFFFF,t_70)
Map集合的第二种遍历思路
1、获取的是Map集合中所有的键值对对象(Entry), 到set集合中,遍历set集合,拿到的是每一个键值对对象(Entry),从这个对象中获取键和值
2、根据Map集合方法可以获取所有键值对对象到一个Set集合中
Set<Map.Entry<K,V>> entrySet()
3、遍历Set集合
迭代器
增强for
4、获取到的就是一个键值对对象
Entry是Map接口中的内部接口,访问方式:Map.Entry
Entry常用的方法
getKey()获取的是键值对中的键
getValue()获取的是键值对中的值
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200806192407636.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTgwNDUzNw==,size_16,color_FFFFFF,t_70)
package com.ujiuye.demos;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Demo_8 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("林青霞", 70);
map.put("张慧敏", 59);
map.put("王祖贤", 60);
map.put("朱茵", 50);
map.put("刘德华", 60);
System.out.println(map);
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
HashMap
1、就是Map集合中使用哈希表存储方式的一种实现类
2、HashMap存储jdk中提供的元素类型的键,直接保证元素的唯一性
3、HashMap存储的键值对中的键是自定义类型元素的时候,无法保证元素的唯一性,要保证自定义元素的唯一性也需要重写hashCode和equals方法
保证键的唯一性和HashSet保证元素唯一性的原理是一样的
4、HashMap和HashSet的关系
HashSet是HashMap实现出来的,HashSet就是HashMap键的那一列
将HashMap值的那一列隐藏掉就成HashSet
package com.ujiuye.demos;
import java.util.HashMap;
import java.util.HashSet;
public class Demo_9 {
public static void main(String[] args) {
HashMap<Person,String> hm = new HashMap<Person, String>();
hm.put(new Person("张三",13),"成都");
hm.put(new Person("张三",13),"成都");
hm.put(new Person("李四",103),"北京");
hm.put(new Person("李四",103),"北京");
hm.put(new Person("王五",25),"武汉");
System.out.println(hm);
}
}
LinkedHashMap
1、是HashMap的一个子类
2、和HashMap的不同之处,具有可预知的迭代顺序,存储键值对的顺序和遍历集合时取出的顺序时的键值对的顺序一致。
package com.ujiuye.demos;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.Set;
public class Demo_10 {
public static void main(String[] args) {
LinkedHashMap<Person,String> lhm = new LinkedHashMap<>();
lhm.put(new Person("张三",13),"成都");
lhm.put(new Person("张三",13),"成都");
lhm.put(new Person("李四",103),"北京");
lhm.put(new Person("李四",103),"北京");
lhm.put(new Person("王五",25),"武汉");
System.out.println(lhm);
Set<Entry<Person, String>> entrySet = lhm.entrySet();
for(Entry<Person, String> entry : entrySet) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
}
四、Collections工具类
1、binarySearch(List<? extends Comparable<? super T>> list, T key)
在一个升序的List集合中,通过二分查找寻找Key对应的索引
2、frequency(Collection<?> c, Object o)
返回指定 collection 中等于指定对象的元素数。
3、replaceAll(List<T> list, T oldVal, T newVal)
使用另一个值替换列表中出现的所有某一指定值。
4、reverse(List<?> list)
反转指定列表中元素的顺序
5、shuffle(List<?> list)将list集合中的元素进行随机置换,打乱
6、sort(List<T> list) 将list集合中的元素进行自然排序
7、synchronizedxxx方法系列:将一个线程不安全的集合传入方法,返回一个线程安全的集合
8、unmodifiablexxx方法系列:将一个可修改的集合传入方法,返回一个只可读的集合
package com.ujiuye.demos;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class Demo_11 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(2);
list.add(2);
list.add(3);
list.add(3);
list.add(3);
list.add(4);
List<String> list1 = new ArrayList<String>();
list1.add("a");
list1.add("b");
list1.add("c");
list1.add("d");
System.out.println(list1);
int binarySearch = Collections.binarySearch(list, 2);
System.out.println(binarySearch);
int frequency = Collections.frequency(list, 3);
System.out.println(frequency);
System.out.println(Collections.max(list1));
System.out.println("----------------------------------");
List<String> list2 = new ArrayList<String>();
list2.add("a");
list2.add("b");
list2.add("c");
list2.add("c");
list2.add("c");
list2.add("d");
Collections.replaceAll(list2, "c", "java");
System.out.println(list2);
Collections.reverse(list1);
System.out.println(list1);
Collections.shuffle(list2);
System.out.println(list2);
Collections.shuffle(list1);
System.out.println(list1);
Collections.sort(list1);
System.out.println(list1);
List<Integer> newList = Collections.unmodifiableList(list);
System.out.println("newList: " + newList);
}
}