Hashset与HashMap

目录

前情回顾

1 set接口

1.1 概述

1.2 Set集合的特点

1.3 常用方法

1.4练习1: Set接口测试

2 HashSet

2.1 概述

2.2练习2: HashSet测试

2.3练习3-1: Set自定义对象去重_创建学生类

2.4练习3-2: Set自定义对象去重_测试

3 Map接口

3.1 概述

3.2 特点

3.3 继承结构

3.4 常用方法

3.5 练习4-1:Map常用方法测试

3.6 练习4-2:Map集合迭代测试

5 HashMap

5.1 前言

5.2概述

5.3 练习5:获取HashMap的数据

5.4 练习6:字符串中字符统计

6拓展

HashMap扩容

Day15
前情回顾
说明: C:\Users\Admin\Desktop\JAVA常用集合关系2.png

1 set接口
1.1 概述

  1. Set是一个不包含重复数据的Collection
    
  2. Set集合中的数据是无序的(因为Set集合没有下标)
    
  3. Set集合中的元素不可以重复 – 常用来给数据去重
    

1.2 Set集合的特点

  1. 数据无序且数据不允许重复
    
  2. HashSet : 底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
    
  3. TreeSet : 底层是TreeMap,也是红黑树的形式,便于查找数据
    

1.3 常用方法
学习Collection接口中的方法即可

1.4练习1: Set接口测试
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Test1_Set.java

package cn.tedu.collection;

import java.util.Arrays;

import java.util.HashSet;

import java.util.Iterator;

import java.util.Set;

/*这个类用来测试Set接口的方法/

public class Test1_Set {

public static void main(String[] args) {

    //1.创建对象

    //Set s = new Set();//报错,Set是接口,接口不可以实例化,也就是创建对象

    Set<String> set = new HashSet<String>();

    //2.set集合数据存放测试

    set.add("牛气冲天");//向set集合添加数据

    set.add("牛气冲天");//向set集合添加重复的数据

    set.add("牛气冲天");//向set集合添加重复的数据

    set.add("虎虎生威");//向set集合添加数据

    //set.add("null");//向set集合添加字符串类型的null数据

    set.add(null);//向set集合添加数据null

   

   

    /**总结1:set集合中的元素都是无序的*/

    /**总结2:set集合中的元素不能重复*/

    /**总结3:set集合中可以存放null元素,也只允许存放0-1个*/

    System.out.println(set);//查看set集合中的元素

   

    //3.set集合常用方法测试

    //set.clear();//清空Set集合

    System.out.println(set.contains("小兔纸"));//false,判断set集合中是否包含指定元素"小兔纸"

    System.out.println(set.equals("牛气冲天"));//false,判断set集合对象与指定元素是否相等

    System.out.println(set.hashCode());//1961052313,获取当前set集合对象的哈希码

    System.out.println(set.isEmpty());//false,判断当前集合是否为空

    System.out.println(set.remove("null"));//false,移除指定元素,没有"null"元素,所以返回false

    System.out.println(set.remove(null));//true,成功移除指定元素null,所以返回true

    System.out.println(set);

    System.out.println(set.size());//2,获取当前set集合的元素个数,类似数组长度

    Object[] array = set.toArray();//把集合中的元素放入数组中

    System.out.println(Arrays.toString(array));//使用数组工具类查看数组中的元素

   

    //4.集合间的操作

    Set<String> set2 = new HashSet();

    set2.add("小老鼠");//给set2集合添加指定元素

    set2.add("小牛犊");//给set2集合添加指定元素

    set2.add("小脑斧");//给set2集合添加指定元素

    set2.add("小兔纸");//给set2集合添加指定元素

    System.out.println(set2);//查看set2集合中的元素

   

    System.out.println(set.addAll(set2));//true,把集合set2中的元素添加到set集合中,成功返回true

    System.out.println(set.containsAll(set2));//true,判断set集合中是否包含set2集合中的所有元素,如果包含返回true

    System.out.println(set.removeAll(set2));//ture,移除set集合中属于set2集合的所有元素

    System.out.println(set.containsAll(set2));//false,判断set集合中是否包含set2集合中的所有元素,不包含返回false

    System.out.println(set.retainAll(set2));

   

    /**retainAll()方法是取两个集合直接的公共部分,谁调用,影响谁*/

// set.add(“小海滕”);

// set2.add(“小海滕”);

// System.out.println(set.retainAll(set));//set没变

// System.out.println(set.retainAll(set2));//set剩set与set2的交集

// System.out.println(set2.retainAll(set));//set2剩set2与set的交集

// System.out.println(set);

// System.out.println(set2);

    //5.集合的迭代

    Iterator<String> it = set2.iterator();//5.1获取集合的迭代器

    while(it.hasNext()) {//5.2判断集合是否有下个元素

        String s = it.next();//5.3如果有,进循环获取当前遍历到的元素

        System.out.println(s);

    }

}

}

2 HashSet
2.1 概述
底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K存入内部的HashMap中,其中K不允许重复,允许使用null.

2.2练习2: HashSet测试
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Test2_HashSet.java

package cn.tedu.collection;

import java.util.HashSet;

import java.util.Iterator;

/*本类用于测试HashSet的用法/

public class Test2_HashSet {

public static void main(String[] args) {

    //1.创建HashSet对象

    HashSet<Integer> set = new HashSet();

   

    //2.向HashSet集合添加元素

    set.add(100);

    set.add(200);

    set.add(300);

   

    set.add(200);

    set.add(200);

    /**总结1:HashSet中的元素没有顺序,且不允许重复*/

    System.out.println(set);

   

    //3.测试常用方法

    //set.clear();//清空set集合

    System.out.println(set.contains(200));//true,判断集合是否包含指定元素

    System.out.println(set.isEmpty());//false,判断集合是否为空

    System.out.println(set.remove(100));//true,移除集合中的指定元素

    System.out.println(set.size());

   

    //4.迭代set集合

    Iterator<Integer> it = set.iterator();//获取集合的迭代器用来遍历

    while(it.hasNext()) {//判断集合中是否有下一个元素,没有则跳出循环

        Integer num = it.next();//获取当前遍历到的元素

        System.out.println(num);//打印当前遍历到的元素

    }

}

}

2.3练习3-1: Set自定义对象去重_创建学生类
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Student.java

package cn.tedu.collection;

/*本类用来测试给set自定义对象去重/

public class Student {

//1.提供对应对象的属性并进行封装

private String name;

private int age;

private String addr;

/**注意1:如果提供了含参/全参构造,需要手动添加无参构造,否则创建对象时必须传参*/

/**注意2:我们可以通过快捷方式生成构造方法:右键--Source--Generate Constructor using fields...*/

/**注意3:给对象的属性赋值,可以通过构造方法[创建对象时直接赋值]/set()[创建对象后调用公共的设置方法赋值]*/

//2.1提供无参构造

public Student() {

    System.out.println("我是无参构造");

}

//2.2提供含参构造

public Student(String name, int age, String addr) {

    super();

    this.name = name;

    this.age = age;

    this.addr = addr;

    System.out.println("我是全参构造");

}

/**注意4:以通过快捷方式生成:右键--Source--Generate Getters and Setters --Select All*/

//3.提供公共的get()/set()

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;

}

public String getAddr() {

    return addr;

}

public void setAddr(String addr) {

    this.addr = addr;

}

/**注意5:重写是为了能够看到对象的属性值

 * 我们可以通过快捷方式生成:右键--Source--Generate to String()...*/

//4.重写toString()

@Override

public String toString() {

    return "Student [name=" + name + ", age=" + age + ", addr=" + addr + "]";

}

//5.重写hashCode() & equlas()

//重写hashCode():我们并不想使用自动计算出的哈希值,而是要根据对象的属性值进行计算,如果两个对象的属性值都相同,想生成同一个哈希码

//重写equals():我们想比较的不是对象的地址值(==),而是如果两个对象的属性值都一样,则返回true

@Override

public int hashCode() {

    final int prime = 31;

    int result = 1;

    result = prime * result + ((addr == null) ? 0 : addr.hashCode());

    result = prime * result + age;

    result = prime * result + ((name == null) ? 0 : name.hashCode());

    return result;

}

@Override

public boolean equals(Object obj) {

    if (this == obj)

        return true;

    if (obj == null)

        return false;

    if (getClass() != obj.getClass())

        return false;

    Student other = (Student) obj;

    if (addr == null) {

        if (other.addr != null)

            return false;

    } else if (!addr.equals(other.addr))

        return false;

    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;

}

}

2.4练习3-2: Set自定义对象去重_测试
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Test3_HashSet2.java

package cn.tedu.collection;

import java.util.HashSet;

import java.util.Set;

/*本类用于给自定义对象去重测试/

//总结:

//1.如果想用set集合给自定义的对象去重,那么需要在自己的类中同时提供重写的hashCode()与equals()

//底层源码: if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))

//重写hashCode():我们并不想使用自动计算出的哈希值,而是要根据对象的属性值进行计算,如果两个对象的属性值都相同,想生成同一个哈希码

//重写equals():我们想比较的不是对象的地址值(==),而是如果两个对象的属性值都一样,则返回true

public class Test3_HashSet2 {

public static void main(String[] args) {

    //1.创建set集合对象

    Set<Student> set = new HashSet<Student>();

    //新版JDK中后面的泛型类型与尖括号都可以不写,三种方式皆可,想用哪个用哪个

// Set set = new HashSet<>();

// Set set = new HashSet();

    //2.1创建自定义对象

    Student s1 = new Student("tony",38,"BeiJing");

    Student s2 = new Student("susan",20,"ShangHai");

    Student s3 = new Student("Jack",3,"ShenZhen");

   

    //创建对象,与之前对象的属性值完全一致

    Student s4 = new Student("susan",20,"ShangHai");

    Student s5 = new Student("Jack",3,"ShenZhen");

    //3.查看两个对象的哈希码

    System.out.println("s2对象的哈希码:"+s2.hashCode());

    System.out.println("s4对象的哈希码:"+s4.hashCode());

   

    //2.2把自定义的student对象添加到set集合中

    set.add(s1);

    set.add(s2);

    set.add(s3);

    set.add(s4);

    set.add(s5);

    /**总结:

     * 重复的给set集合添加了属性相同的对象,为什么没有像之前那样去重呢?

     * 翻阅源码,得知:需要保证两个条件:

     * 1.保证对象拥有相同的哈希码值

     * --底层默认使用的是Object提供的hashCode()来计算哈希码值,每次new对象,默认的哈希码值是不同的

     * 解决方案:如果想根据两个对象的属性值来计算哈希值,就需要重写hashCode()

     * 2.保证两个对象的equals()返回true

     * --底层默认使用的是Object提供的逻辑,==比较,也就是说当地址值相同时,才返回true

     * 解决方案:重写equals()

     * */

    System.out.println(set);

   

}

}

说明: C:\Users\Admin\AppData\Roaming\Tencent\Users\2851318847\QQEIM\WinTemp\RichOle\PBR8F@~%1TIW@7{52TBF6%Q.png

说明: C:\Users\Admin\AppData\Roaming\Tencent\Users\2851318847\QQEIM\WinTemp\RichOle\OYXHGJUPFP79CGH]N)]UU[U.png

3 Map接口
3.1 概述
Java.util接口Map<K,V>

  类型参数 : K - 表示此映射所维护的键  V – 表示此映射所维护的对应的值

  也叫做哈希表、散列表. 常用于键值对结构的数据.其中键不能重复,值可以重复

说明: fc2fe4853a9fdd98b2b5c058978ad25

3.2 特点
1. Map可以根据键来提取对应的值

  2. Map的键不允许重复,如果重复,对应的值会被覆盖

  3. Map存放的都是无序的数据

  4. Map的初始容量是16,默认的加载因子是0.75

TIPS:源码摘抄:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

初始容量1<<4,相当于1*(2^4),也就是16

static final float DEFAULT_LOAD_FACTOR = 0.75f;

默认的加载因子是0.75f,也就是存到75%开始扩容,按照2的次幂进行扩容

3.3 继承结构

3.4 常用方法
学习Collection接口中的方法即可

void clear() 从此映射中移除所有映射关系(可选操作)。

boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。

boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。

Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。

boolean equals(Object o) 比较指定的对象与此映射是否相等。

V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。

int hashCode() 返回此映射的哈希码值。

boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true。

Set keySet() 返回此映射中包含的键的 Set 视图。

V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。

void putAll(Map<? extends K,? extends V> m)从指定映射中将所有映射关系复制到此映射中(可选操作)。

V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。

int size() 返回此映射中的键-值映射关系数。

Collection values() 返回此映射中包含的值的 Collection 视图。

3.5 练习4-1:Map常用方法测试
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Test4_Map.java

package cn.tedu.collection;

import java.util.Collection;

import java.util.HashMap;

import java.util.Map;

/*本类用于测试Map接口/

public class Test4_Map {

public static void main(String[] args) {

    //1.创建Map对象

    //map中的数据要符合映射规则,一定注意要同时指定K和V的数据类型

    //至于K和V需要指定成什么类型的数据,取决于你的具体需求

    Map<Integer,String> map = new HashMap();//注意导包:java.util

    //2.常用方法测试

    //添加数据,需要同时指定K和V

    map.put(9527, "白骨精");//向map集合添加数据

    map.put(9528, "黑熊精");//向map集合添加数据

    map.put(9528, "唐三藏");

    map.put(9529, "者行孙");

    /**

     * 总结1:Map存放的都是无序数据

     * 总结2:Map中的key不可以重复,如果重复,此Key对应的值会被覆盖

     *map打印结果: {9527=白骨精, 9528=唐三藏, 9529=者行孙}

     */

    //查看map集合中的元素

    System.out.println(map);

    //map.clear();//清空map集合

    System.out.println(map.containsKey(9527));//true,判断当前map集合是否包含指定的key

    System.out.println(map.containsValue("土地老儿"));//false,判断当前map集合是否包含指定的value

    System.out.println(map.equals("者行孙"));//false,判断"者行孙"与map是否相等

    System.out.println(map.get(9529));//者行孙,根据对应的key来获取对应的value

    System.out.println(map.hashCode());//84598429,获取当前map集合的哈希码

    System.out.println(map.isEmpty());//false,判断当前map集合是否为空

    System.out.println(map.remove(9529));//者行孙,删除map中key对应的value,正确删除后返回被删除元素

    System.out.println(map.get(9529));//null,没有拿到任何元素,根据指定的key获取对应value

    System.out.println(map.size());//2,获取集合中元素的个数

    Collection<String> values = map.values();//把map中的所有value收集起来放到collection中

    System.out.println(values);//[白骨精, 唐三藏]

}

}

说明: C:\Users\Admin\Desktop\Map集合迭代方式.png

3.6 练习4-2:Map集合迭代测试
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Test4_Map.java

package cn.tedu.collection;

import java.util.Collection;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

/*本类用于测试Map接口/

public class Test4_Map {

public static void main(String[] args) {

    //1.创建Map对象

    //map中的数据要符合映射规则,一定注意要同时指定K和V的数据类型

    //至于K和V需要指定成什么类型的数据,取决于你的具体需求

    Map<Integer,String> map = new HashMap();//注意导包:java.util

    //2.常用方法测试

    //添加数据,需要同时指定K和V

    map.put(9527, "白骨精");//向map集合添加数据

    map.put(9528, "黑熊精");//向map集合添加数据

    map.put(9528, "唐三藏");

    map.put(9529, "者行孙");

    /**

     * 总结1:Map存放的都是无序数据

     * 总结2:Map中的key不可以重复,如果重复,此Key对应的值会被覆盖

     *map打印结果: {9527=白骨精, 9528=唐三藏, 9529=者行孙}

     */

    //查看map集合中的元素

    System.out.println(map);

    //map.clear();//清空map集合

    System.out.println(map.containsKey(9527));//true,判断当前map集合是否包含指定的key

    System.out.println(map.containsValue("土地老儿"));//false,判断当前map集合是否包含指定的value

    System.out.println(map.equals("者行孙"));//false,判断"者行孙"与map是否相等

    System.out.println(map.get(9529));//者行孙,根据对应的key来获取对应的value

    System.out.println(map.hashCode());//84598429,获取当前map集合的哈希码

    System.out.println(map.isEmpty());//false,判断当前map集合是否为空

    System.out.println(map.remove(9529));//者行孙,删除map中key对应的value,正确删除后返回被删除元素

    System.out.println(map.get(9529));//null,没有拿到任何元素,根据指定的key获取对应value

    System.out.println(map.size());//2,获取集合中元素的个数

    Collection<String> values = map.values();//把map中的所有value收集起来放到collection中

    System.out.println(values);//[白骨精, 唐三藏]

   

    //对map集合进行迭代

    /**方式一

     * 遍历map中的数据,需要把map集合转换成set集合

     * Set<K> keySet() : 把map集合中的所有的key存到set集合中

     * */

    Set<Integer> keySet = map.keySet();

    //想要遍历set集合,需要先拿到集合的迭代器对象

    Iterator<Integer> it = keySet.iterator();

    while(it.hasNext()) {//判断集合中是否有下个元素,如果有,继续迭代,如果没有,跳出循环

        Integer key = it.next();//依次获取/set集合中的每一个key

        String value = map.get(key);//通过key获取对应的value

        System.out.println("{" + key + "," + value + "}");

    }

    /**方式二

     * 遍历map中的数据,需要把map集合转换成set集合

     * Set<Entry<Integer,String>> entrySet() : 把map集合中的一组key&value数据整体放入set中

     * 一对儿 K,V 是一个Entry

     */

    Set<Entry<Integer, String>> entrySet = map.entrySet();

    //遍历set集合,得到每个Entry对象

    //获取此set集合对应的迭代器对象it2

    Iterator<Entry<Integer, String>> it2 = entrySet.iterator();

    while(it2.hasNext()) {

        Entry<Integer, String> entry = it2.next();

        Integer key = entry.getKey();

        String value = entry.getValue();

        System.out.println("[" + key + ":" + value + "]");

    }

}

}

5 HashMap
5.1 前言
HashMap的键要同时重写hashCode()和equlas()

hashCode()用来判定二者的hash值是否相同,重写后根据属性生成

equlas()用来判断属性的值是否相同,重写后,根据属性判断

–equlas()判断数据如果相等,hashCode()必须相同

–equlas()判断数据如果不等,hashCode()尽量不同

5.2概述
HashMap底层是一个Entry[ ]数组,当存放数据时,会根据hash
算法来计算数据的存放位置

  算法:hash(key)%n , n就是数组的长度,其实也就是集合的容量

  当计算的位置没有数据的时候,会直接存放数据

  当计算的位置,有数据时,会发生hash冲突/hash碰撞,解决的办法就
  是采用链表的结构,在对应的数据位置存放链表的头节点,对于
  这个链表来说,每次新加的节点会从头部位置开始加入,也就是说,
  数组中的永远是新节点.

说明: C:\Users\Admin\Desktop\hashMap图解.png

5.3 练习5:获取HashMap的数据
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Test5_HashMap.java

package cn.tedu.collection;

import java.util.HashMap;

/*本类用于HashMap的练习/

public class Test5_HashMap {

public static void main(String[] args) {

    //1.创建HashMap对象

    HashMap<Integer,String> map = new HashMap();

    /**

     * 源码摘抄:

     * static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

     * 初始容量为1<<4,相当于1*(2^4)=16

     * static final float DEFAULT_LOAD_FACTOR = 0.75f;

     * 默认的加载因子是0.75,也就是说存到75%开始扩容,按照2的次幂进行扩容

     */

    /*

     * 达到容量的加载因子后,就会重新开辟空间,重新计算所有对象的存储位置,也叫做rehash

     * 设置初始容量与加载因子要讲求相对平衡,如果加载因子过低,则rehash过于频繁,影响性能

     * 如果初始容量设置太高或者加载因子设置太高,影响查询效率

     */

}

}

5.4 练习6:字符串中字符统计
创建Java工程: DAY15

创建包: cn.tedu.collection

创建类: Test6_MapTest.java

package cn.tedu.collection;

import java.util.HashMap;

import java.util.Map;

import java.util.Scanner;

/**

  • 本类用来完成Map集合相关练习

  • 需求:提示并接收用户输入的一串字符,并且统计出每个字符出现的次数

  • */

public class Test6_MapTest {

public static void main(String[] args) {

    //1.提示用户输入要统计的字符串

    System.out.println("请输入您要统计的字符串");

    //2.接收用户输入的要统计的字符串

    String input = new Scanner(System.in).nextLine();

    //3.获取到用户输入的每个字符,String底层维护的是char[]

    //创建map集合存放数据,格式:{b=2,d=4,g=3}

    /**统计的是每个字符出现的次数,所以字符是char类型,次数是int,但是不可以使用基本类型,需要使用包装类型*/

    Map<Character,Integer> map = new HashMap();

   

    //开始位置:0 - 数组的第一个元素

    //结束位置:<input.length() 或者 <=input,length()-1

    //如何变化:++

    for (int i = 0; i < input.length(); i++) {

        char key = input.charAt(i);//获取一串字符中指定位置上的字符

        System.out.println("获取到的第"+(i+1)+"个字符:"+key);

       

        //4.统计每个字符出现的个数,存起来,存到map

        Integer value = map.get(key);//要先拿着key到map中找是不是有value

        if(value == null) {//如果判断为null,说明之前没有存过这个字符

            map.put(key, 1);//把当前的字符作为key存入,次数存1

        }else {

            map.put(key,value+1);//如果存过值,给之前的次数+1

        }

    }

    System.out.println("各个字符出现的频率为:");

    System.out.println(map);

}

}

6拓展
HashMap扩容
加载因子:

static final float DEFAULT_LOAD_FACTOR = 0.75f;

前面的讲述已经发现,当你空间只有仅仅为10的时候是很容易造成2个对象的hashcode 所对应的地址是一个位置的情况。这样就造成 2个 对象会形成散列桶(链表)。这时就有一个加载因子的参数,值默认为0.75 ,如果你hashmap的 空间有 100那么当你插入了75个元素的时候 hashmap就需要扩容了,不然的话会形成很长的散列桶结构,对于查询和插入都会增加时间,因为它要一个一个的equals比较。但又不能让加载因子很小,如0.01,这样显然是不合适的,频繁扩容会大大消耗你的内存。这时就存在着一个平衡,jdk中默认是0.75,当然负载因子可以根据自己的实际情况进行调整。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值