原文:https://www.cnblogs.com/Burning-youth/p/15610472.html
-
【JAVA】笔记(16)---集合(5)- 详解 Set集合( Map 体系集合常用方法;哈希表;
哈希表:
- 博主说不明白,博主百度,博主陷入尴尬 ....
- hash表的本质其实就是数组,hash表中通常存放的是键值对Entry;哈希表就是根据 key 值来通过哈希函数计算得到一个值,这个值就是下标值,用来确定这个Entry要存放在哈希表中哪个位置;
哈希碰撞:
- hash碰撞指的是,两个不同的值(比如张三、李四的学号)经过hash计算后,得到的hash值相同,后来的李四要放到原来的张三的位置,但是数组的位置已经被张三占了,导致冲突。
二叉树数据结构:
Set 集合:
1.Set:(Collection<----Set)
1)Set 体系下的所有集合存储元素特点:无序不可重复(放进去和取出来的顺序不同);
2)Set 集合元素无下标;
2.HashSet:(Collection<----Set<---HashSet)
其实就是 new 了一个 HashMap 集合。向 HashSet 中储存元素,其实是放到了 HashMap 集合中;
3.SortedSet:(Collection<----Set<---SortedSet)
增加了可以将集合元素按大小排序的特性;
4.TreeSet:(Collection<----Set<---TreeSet)
其实就是 new 了一个 TreeMap 集合。向 TreeSet 中储存元素,其实是放到了 TreeMap 集合中;
Map 集合:
Map:
1.Map 集合与 Collection 集合 没有任何关系;
2.Map 集合以 key-value 键值对的方式储存元素;
3.key 和 value 储存的都是对象的内存地址;
4.key 无序且不可重复;
Map常用方法:
1.V put ( key , value ) //将键值对放到 HashMap 中(元素重复则不再载入)
2.V get(Object key) // 通过key获取value
3.void clear ( ) //清空Map集合
4.boolean containsKey (Object key) //判断Map 中是否包含某 key
5.boolean containsValue(Object value)// 判断Map 中是否包含某个value
6.boolean isEmpty ( ) // 判断Map集合中元素个数是否为0
7.V remove(0bject key) // 通过key 删除键值对
8.int size ( ) //获取Map 集合中键值对的个数
9.Set<K> keySet ( ) // 获取Map 集合所有的key (所有的键是一个set集合)
10.Set <Map. Entry <K,V> > entrySe t( )
//将Map集合的key和value都转换为String,并拼接到一起(键值之间还拼接了个“=”),放到Set集合中,最后返回此Set集合
这个返回的 Set 集合中的每个元素类型都是 Map . Entry(Map中的内部类)
11.Collection<V> values ( ) // 获取Map 集合中所有的value ,装到 Collection 里,并返回此集合
12.Map . get (Object key ) //返回 key 所对应的 value
13.Map . Entry <key类型 , value类型 > // 此为Map中的一个内部类,内部类中有 getKey ( ) , getValue ( ) 等方法
Map 使用须知:
1.向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!equals方法有可能调用,也有可能不调用;
1)拿 put ( k , v ) 举例,什么时候 equals 不会调用 ?
k. hashCode ( ) 方法返回哈希值,哈希值经过哈希算法转换成数组下标;数组下标位置上如果是null.,,equals不需要执行;
2)get ( k ) 举例,什么时候equals不会调用?
k. hashCode ( ) 方法返回哈希值,哈希值经过哈希算法转换成数组下标。数组下标位置上如果是null. equals不需要执行。
2.注意:
如果一个类的equals方法重写了,那么 hashCode ( ) 方法必须重写。并且equals方法返回如果是true , hashCode ( ) 方法返回的值必须一样;
equals方法返回true表示两个对象相同,在同一个单向链表上比较;那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。所以hashCode()方法的返回值也应该相同。
3.hashCode ( ) 方法和equals ( ) 方法不用细研究,直接使用IDEA工具生成,但是这两个方法需要同时生成。
4.结论:放在 HashMap 集合 key 部分的 , 以及放在 HashSet 集合中的元素 , 需要同时重写 hashCode 方法和 equals方法。
5.对于哈希表数据结构来说:
如果o1和o2的hash值相同,一定是放到同一个单向链表上;
当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”;
栗子老师:四种遍历Map集合的方法
import java.util.*;
public class practice {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap();
map.put(1, "value1");
map.put(2, "value2");
map.put(3, "value3");
//第一种:二次取值(效率低)
System.out.println("---Map.keySet(),Map.get(key) 二次取值---");
for (Integer key : map.keySet()) {
System.out.println( key + "=" + map.get(key));
}
//第二种:
System.out.println("\n---Map.entrySet(),迭代器 遍历key,value---");
Iterator map1it = map.entrySet().iterator();
while (map1it.hasNext()) {
Map.Entry<Integer, String> entry = (Map.Entry<Integer, String>) map1it.next();
System.out.println( entry.getKey() + "=" + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("\n---Map.entrySet(),foreach 遍历key,value---");
for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println( entry.getKey() + "=" + entry.getValue());
}
//第四种:
System.out.println("\n---Map.values() 遍历所有的value,但不能遍历key---");
for (String v : map.values()) {
System.out.println(v);
}
}
}
运行结果:
------------------------------------
---Map.keySet(),Map.get(key) 二次取值---
1=value1
2=value2
3=value3
---Map.entrySet(),迭代器 遍历key,value---
1=value1
2=value2
3=value3
---Map.entrySet(),foreach 遍历key,value---
1=value1
2=value2
3=value3
---Map.values() 遍历所有的value,但不能遍历key---
value1
value2
value3
Process finished with exit code 0
HashMap:(Map<---HashMap)
1.底层采用了哈希表数据结构;
2.非线程安全;
3.在默认的情况下,HashMap的容量是16,调用构造函数来初始化容量,一般为 2 的 n 次幂;默认加载因子为 0.75;扩容后,容量为原来的2倍;
4.在 JDK 8 之后,如果哈希表单向链表中元素超过8个,数据结构就会由单向链表变为红黑树数据结构;当红黑树上的节点数量小于6时,红黑树又会变为单向链表数据结构;这种特性也是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率;
5.HashMap 的 key 和 value 允许为null;
Hashtable:(Map<---Hashtable)
1.底层采用了哈希表数据结构,初始化容量为11,扩容为 原容量的2倍+1;
2.所有方法被 synchronized 关键词修饰,所以是线程安全的,效率较低,由于现在有其他更好保证线程安全的方案,所以 Hashtable 使用较少;
3.Hashtable 的 key 和 value 不允许为null;
Properties:(Map<---Hashtabl<---Properties)
1.Properties 被成为属性类;key 和 value 只支持 String 类型;
2.栗子老师:
package com.bjpowernode.javase.day2;
import java.util.Properties;
public class practice {
public static void main(String[] args) {
Properties properties=new Properties();
//添加元素
properties.setProperty("账户","ZBC123");
properties.setProperty("密码","123456");
//查找元素
System.out.println(properties.getProperty("账户"));
System.out.println(properties.getProperty("密码"));
}
}
运行结果;
--------------------------------
ZBC123
123456
Process finished with exit code 0
SortedMap:(Map<---SortedMap)
放在 SortedMap 集合中的元素 key 部分会自动按大小排序;
TreeMap:(Map<---SortedMap<---TreeMap)
1.底层采用二叉树数据结构;
2.按大小自动排序的规则如何修改?
1)自定义类实现 Comparable <> 接口,再重写 compareTo ( ) 方法;
import java.util.*;
public class practice {
public static void main(String[] args) {
TreeSet<People> peoples=new TreeSet<>();
peoples.add(new People(10,"zhangsan"));
peoples.add(new People(11,"lisi"));
peoples.add(new People(10,"zhangsi"));
for (People people:peoples) {
System.out.println(people);
}
}
}
//规则:优先从小到大排序年龄;
// 年龄相同,按首字母先后比较 name
class People<E> implements Comparable<People>{
private int old;
private String name;
public People() {
}
public People(int old, String name) {
this.old = old;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"old=" + old +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
People people = (People) o;
return old == people.old &&
Objects.equals(name, people.name);
}
@Override
public int hashCode() {
return Objects.hash(old, name);
}
@Override
public int compareTo(People o) {
if (this.old==o.old){
return this.name.compareTo(o.name);
}
return this.old-o.old;
}
}
运行结果:
---------------------------------
Student{old=10, name='zhangsan'}
Student{old=10, name='zhangsi'}
Student{old=11, name='lisi'}
Process finished with exit code 0
2)在创建 TreeSet 对象时,实现匿名内部类 Comparator,并重写 compare(object o1, object o2)方法
import java.util.*;
public class practice {
public static void main(String[] args) {
//规则:优先从小到大排序年龄;
//年龄相同,按首字母先后比较 name
TreeSet<People> peoples = new TreeSet<>(new Comparator<People>() {
@Override
public int compare(People o1, People o2) {
if (o1.getOld() == o2.getOld()) {
return o1.getName().compareTo(o2.getName());
}
return o1.getOld() - o2.getOld();
}
});
peoples.add(new People(10,"zhangsan"));
peoples.add(new People(11,"lisi"));
peoples.add(new People(10,"zhangsi"));
for (People people:peoples) {
System.out.println(people);
}
}
}
class People<E> {
private int old;
private String name;
public People() {
}
public People(int old, String name) {
this.old = old;
this.name = name;
}
public int getOld() {
return old;
}
public void setOld(int old) {
this.old = old;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"old=" + old +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
People people = (People) o;
return old == people.old &&
Objects.equals(name, people.name);
}
@Override
public int hashCode() {
return Objects.hash(old, name);
}
}
运行结果:
--------------------------
同上,不信可以去试试哦 ~ ~
3)自定义比较器对象,并实现 Comparator< > 接口,重写 compare 方法,然后在 TreeSet 的构造方法中 new 出比较器对象
package com.bjpowernode.javase.day2;
import java.util.*;
public class practice {
public static void main(String[] args) {
TreeSet<People> peoples = new TreeSet<>(new PeopleComparator());
peoples.add(new People(10,"zhangsan"));
peoples.add(new People(11,"lisi"));
peoples.add(new People(10,"zhangsi"));
for (People people:peoples) {
System.out.println(people);
}
}
}
class People<E> {
private int old;
private String name;
public People() {
}
public People(int old, String name) {
this.old = old;
this.name = name;
}
public int getOld() {
return old;
}
public void setOld(int old) {
this.old = old;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"old=" + old +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
People people = (People) o;
return old == people.old &&
Objects.equals(name, people.name);
}
@Override
public int hashCode() {
return Objects.hash(old, name);
}
}
//规则:优先从小到大排序年龄;
//年龄相同,按首字母先后比较 name
class PeopleComparator implements Comparator<People>{
@Override
public int compare(People o1, People o2) {
if (o1.getOld() == o2.getOld()) {
return o1.getName().compareTo(o2.getName());
}
return o1.getOld() - o2.getOld();
}
}
运行结果:
------------------------------
同上上
随笔:
在自定义类实现了 Comparable <> 接口,并重写了 compareTo ( ) 方法 的前提下,使用 Collection 工具类 ,也可以达到排序的目的:
1)Collections.sort(集合) ;
public static void main(String[] args) {
List<People> peoples=new ArrayList<>();
peoples.add(new People(10,"zhangsan"));
peoples.add(new People(11,"lisi"));
peoples.add(new People(10,"zhangsi"));
Collections.sort(peoples);
for (People people:peoples) {
System.out.println(people);
}
}
2)Collections.sort ( 集合 ,new 集合对应的比较器对象 );
public static void main(String[] args) {
List<People> peoples=new ArrayList<>();
Collections.sort(peoples,new PeopleComparator());
peoples.add(new People(10,"zhangsan"));
peoples.add(new People(11,"lisi"));
peoples.add(new People(10,"zhangsi"));
for (People people:peoples) {
System.out.println(people);
}
}