目录
Map
Map常用方法的使用
Set
Set常用方法的使用
例题
二叉搜索树
哈希表
哈希冲突
避免冲突
小结
1、V put(K key, V value) //设置 key 对应的 value
map.put(1,"hello");
map.put(2,"world");
//结果:{1=hello, 2=world}
2、V get(Object key) //返回 key 对应的 value
map.get(1);
//结果:hello
3、V getOrDefault(Object key, V defaultValue) //返回 key 对应的 value,key 不存在,返回默认值
String str=map.getOrDefault(2,"hi");
String str1=map.getOrDefault(3,"hi");
//结果:
//str world
//str1 hi
4、V remove(Object key) //删除 key 对应的映射关系
map.remove(1);
System.out.println(map);
//结果:{2=world}
5、Set
map.put(1,"hi");
map.put(2,"thanks");
map.put(2,"what");
map.put(3,"how");
System.out.println(map.keySet());
//结果:[1, 2, 3]
6、Set
map.put(1,"hi");
map.put(2,"thanks");
map.put(3,"how");
for(Map.Entry entry : map.entrySet()){
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
System.out.println();
//结果:
1--->hi
2--->thanks
3--->how
7、boolean containsKey(Object key) //判断是否包含 key
map.put(1,"hi");
map.put(2,"thanks");
map.put(3,"how");
map.containsKey(1);//true
map.containsKey(5);//false
map.containsValue("hi");//true
map.containsValue("hello");//false
K getKey() //返回 entry 中的 key
V getValue() //返回 entry 中的 value
V setValue(V value) //将键值对中的value替换为指定value
注意:
2. Map中存放键值对的Key是唯一的,value是可以重复的 。
4. Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
5. Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
1、boolean add(E e) //添加元素,但重复元素不会被添加成功
Set set=new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
boolean flg=set.add(1);
System.out.println(flg);
System.out.println(set);
//结果
//false
//[1, 2, 3]
2、boolean contains(Object o) //判断 o 是否在集合中
boolean flg=set.contains(1);//true
boolean flg1=set.contains(6);//false
3、Iterator
set.add(1);
set.add(2);
set.add(3);
Iterator it= set.iterator();
while(it.hasNext()){
System.out.print(it.next()+" ");
}
//结果:1 2 3
4、boolean remove(Object o) //删除集合中的 o
set.add(1);
set.add(2);
set.add(3);
boolean flg2=set.remove(1);
System.out.println(flg2);//true
System.out.println(set);//[2, 3]
5、int size() //返回set中元素的个数
set.add(1);
set.add(2);
set.add(3);
System.out.println(set.size());//3
6、boolean isEmpty() //检测set是否为空,空返回true,否则返回false
7、Object[] toArray() //将set中的元素转换为数组返回
Set
5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础 上维护了一个双向链表来记录元素的插入次序。
1、在1_0000个数据里面找重复的数据
public static Map func(int[] array){
//判断array中的元素是否在map中,如果不在就是1,如果在就在原来的基础上加1
Map map=new HashMap<>();
for(int x:array){
if(map.get(x)==null){
map.put(x,1);
}else{
int val=map.get(x);
map.put(x,val+1);
}
}
return map;
}
public static void main(String[] args) {
int[] array=new int[10000];
Random random=new Random();
for(int i=0;i< array.length;i++){
array[i]=random.nextInt(1000);//生成的随机数在0-1000之间,一定有重复的元素
}
Map map=func(array);
System.out.println(map);
String str="array";
System.out.println(str.toCharArray());
}
2、将10000个数据去重
public static Set func(int[] array){
Set set=new HashSet<>();
for (int x:array) {
set.add(x);
}
return set;
}
public static void main(String[] args) {
int[] array=new int[10000];
Random random=new Random();
for (int i = 0; i < array.length; i++) {
array[i]=random.nextInt(1000);
}
Set set=func(array);
System.out.println(set);
int ret=func1(array);
System.out.println(ret);
}
查找
在二叉搜索树中查找某个值key,就用key和根节点比较,key更大,在右子树查找,key更小,在左子树查找。
代码示例:
public Node search(int key){
Node cur=root;
while(cur!=null){
if(key>cur.val){
cur=cur.right;
}else if(key
插入
在二叉搜索树中插入某个结点,要不破坏它的性质,所以要先找到合适插入的位置,然后再插入
public boolean insert(int val){
//空树情况下
if(root==null){
root=new Node(val);
return true;
}
//找到要插入的位置
Node cur=root;
Node parent=null;//记录父节点
while(cur!=null) {
if (val > cur.val) {
parent = cur;
cur = cur.right;
} else if (val < cur.val) {
parent = cur;
cur = cur.left;
} else {
return false;//不能有相同数据
}
}
//插入
Node tmp=new Node(val);
if(val
删除
删除二叉搜索树中某个结点,先找到要删除的位置,在进行删除
代码示例:
public void remove(int key){
Node cur=root;
Node parent=null;
//找到要删除结点位置
while(cur!=null){
if(cur.val==key){
removeNode(cur,parent);
break;
}else if(cur.val
该方式即为哈希( 散列 ) 方法, 哈希方法中使用的转换函数称为哈希 ( 散列 ) 函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)。
我们可以看到4和14通过相同哈希函数找到了相同的位置,发生了冲突。
此时不同的关键字通过相同的哈希函数有可能找到相同的位置。此时的情况就叫: 哈希冲突/哈希碰撞。
散列表的载荷因子定义为: a =填入表中的元素个数/散列表的长度。
a是散列表装满程度的标志因子。由于表长是定值,a与“填入表中的元素个数”成正比,所以,a越大,表明填入表中的元素越多,产生冲突的可能性就越大。反之,a越小,标明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是载荷因子a的函数,只是不同处理冲突的方法有不同的函数。对于开放定址法,荷载因子是特别重要因素,应严格限制在0. 7-0.8以下。超过0.8,查表时的CPU缓存不命中( cachemissing)按照指数曲线上升。因此,一些采用开放定址法的hash库,如Java的系统库限制了荷载因子为0.75,超过此值将resize散列表。
随着负载因子的增大,冲突率也在增大,所以当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来变相的降低冲突率。已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小。
闭散列
但是线性探测有一个不好的地方,它依次向后探测,直到寻找到下一个空位置为止,如果空位置是连续的那么产生冲突的元素都放在一起了。
开散列
实现
public class HashBuck {
static class Node{
public int key;
public int val;
Node next;
public Node(int key,int val){
this.key=key;
this.val=val;
}
}
public Node[] array;
public int usedSize;
public static final double DEFAULT_LOAD_FACTOR = 0.75;//负载因子
public HashBuck(){
this.array=new Node[10];
}
/**
* put函数
* @param key
* @param val
*/
public void put(int key,int val){
//1、确认key所在位置
int index=key% array.length;
Node cur=array[index];
//2、遍历这个下标的链表,看是不是有相同的key,有的话要更新val值
while(cur!=null){
if(cur.key==key){
cur.val=val;
return;
}
cur=cur.next;
}
//3、没有key这个元素,用头插法插入
Node node=new Node(key,val);
node.next=array[index];
array[index]=node;
this.usedSize++;
//4、插入元素成功之后,检查当前散列表的负载因子
if(loadFactor()>=DEFAULT_LOAD_FACTOR){
resize();//扩容
}
}
private void resize(){
Node[] newArray=new Node[array.length*2];
for(int i=0;i< newArray.length;i++){
Node cur=newArray[i];
while(cur!=null){
int index= cur.key% newArray.length;//获取新下标
//就是把cur这个节点,以头插的形式 插入到新的数组对应下标的链表当中
Node curNext=cur.next;
cur.next=newArray[index];
newArray[index]=cur;
cur=curNext;
}
}
array=newArray;
}
private double loadFactor() {
return 1.0*usedSize/ array.length;
}
/**
* 根据key获得val值
* @param key
* @return
*/
public int get(int key){
//1、确认key所在位置
int index=key% array.length;
Node cur=array[index];
//2、遍历这个下标的链表,找到key,返回当前val值
while(cur!=null){
if(cur.key==key){
return cur.val;
}
cur=cur.next;
}
return -1;
}
}
class Person{
public String ID;
public Person(String ID){
this.ID=ID;
}
//重写
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(ID, person.ID);
}
@Override
public int hashCode() {
return Objects.hash(ID);
}
@Override
public String toString() {
return "Person{" +
"ID='" + ID + '\'' +
'}';
}
}
public class HashBuck2 {
static class Node{
public K key;
public V val;
public Node next;
public Node(K key,V val){
this.key=key;
this.val=val;
}
}
public Node[] array=( Node[])new Node[10];
int usedSize;
/**
* put函数
* @param key
* @param val
*/
public void put(K key,V val){
int hash=key.hashCode();
int index=hash% array.length;
Node cur=array[index];
while(cur!=null){
//利用equals进行相等性比较
if(cur.key.equals(key)){
cur.val=val;
return;
}
cur=cur.next;
}
Node node=new Node<>(key,val);
node.next=array[index];
array[index]=node;
this.usedSize++;
}
/**
* 根据key获得val值
* @param key
* @return
*/
public V get(K key){
int hash=key.hashCode();
int index=hash% array.length;
Node cur=array[index];
while(cur!=null){
if(cur.key.equals(key)){
return cur.val;
}
cur=cur.next;
}
return null;
}
常见问题:(看源码)
1.如果new HashMap(19), bucket数组多大?
>=19 &&最接近19的一个2次幂--32
2. HashMap什么时候开辟bucket数组占用内存?
第一次put的时候--16
3. hashMap何时扩容?
超过负载因子的时候扩容,且是2倍扩容的
4.当两个对象的hashcode相同会发生什么?
冲突
5.如果两个键的hashcode相同,你如何获取值对象?
遍历与hashCode值相等时相连的链表,直到相等(equals)或者null(没有找到)
6.你了解重新调整HashMap大小存在什么问题吗?
重新哈希原来的元素到新的链表当中
以上就是今天的内容了,有什么问题都可以在评论区留言哦✌✌✌