Surpass Day23——Java Map接口、HashMap接口、HashCode集合、Properties类、TreeSet集合

目录

1关于Map接口

1.1 关于Map接口

1.2 Map接口中常用的方法

1.3 遍历Map集合

2、HashMap集合

2.1 关于HashMap集合

2.2 HashMap集合的key部分特点

2.3 哈希表HashMap使用不当无法发挥性能

2.4 map.put(k,v)实现原理

2.5 v = map.get(k)实现原理

2.6 HashMap集合的初始化容量和扩容

2.7 euquals和hashCode的重写

3、HashCode集合

4、Properties类

5、TreeSet集合

5.1 关于TreeSet集合

5.2 排序方式:实现Comparable接口

5.3 二叉树数据结构

5.4 排序方式:实现Comparator比较器接口

5.5 Comparable接口和Comparator比较器接口的选择

6、Collections工具类


1、Map接口

1.1 关于Map接口

1)MapCollection没有继承关系。

2)Map集合以key和value的方式存储数据:键值对

key和value都是引用数据类型。

key和value都是存储对象的内存地址。

key起到主导的地位,value是key的一个附属品

1.2 Map接口中常用的方法

V put(K key,V value) 向Map集合中添加键值对

V get(Object key) 通过key获取value

void clear() 清空Map集合

boolean containsKey(Object key) 判断Map中是否包含某个key

boolean containsValue(Object value) 判断Map中是否包含某个value

boolean isEmpty() 判断Map集合中元素个数是否为e

Set<K> keySet() 获取Map集合所有的key(所有的键是一个set集合)

V remove(Object key) 通过key删除键值对

int size() 获取Map集合中键值对的个数。

Collection<V> values() 获取Map集合中所有的value,返回一个Collection

Set<Map.Entry <K,V> >entrySet() 将Map集合转换成set集合

注意:关于将Map集合转换成set集合Set<Map.Entry<K,V>>entrySet()

假设现在有一个Map集合,如下所示:

map1集合对象
key             value
1                  zhangsan
2                   lisi
3                   wangwu
4                   zhaoliu

Set set = map1.entrySet();

set集合对象
1=zhangsan【转换成的这个Set集合,Set集合的类型是Map.Entry(一种类型的名字)】
2=lisi
3=wangwu
4=zhaoliu

Entry是Map中的静态内部类

// 创建Map集合对象
Map<Integer, String> map = new HashMap<>();
// 向Nap集合中添加键值对
map.put(1,"zhangsan");// 1在这里进行了自动装箱。
map.put(2,"lisi");
map.put(3,"wangwu");
map.put(4, "zhaoliu");
// 通过key获取value
String value = map.get(2);
System.out.println(value);
//获取键值对的数量
System.out.println("键值对的数量:"+map.size());|
// 通过key删除key-value
map.remove(key: 2);
System.out.println("键值对的数量:"+map.size());
// 判断是否包含某个key
//contoins方法屈层调用的都是equals进行比对的,所以自定义的类型需要重写equals方法。
System.out.println(imap.containsKey(new Integer( value: 4))); // true
//判断是否包含某个value
System.out.println(map.containsValue(new String( original: "wangwu"))); // trud
​
//获取所有的value
Collection<String> values = map.value();
for(String s:values){
    System.out.println(s);
}
//清空Map集合
map.clear();
System.out.println("键值对的数量:"+map.size());

1.3 遍历Map集合

第一种方式:获取所有的key,通过遍历key,来遍历value

Map<Integer, String> map = new MashMap<>();
map.put(1,"zhangsan");
map.put(2,"1isi");
map.put(3,"wangwu");
map.put(4,"zhaoliu");
//迎历Nap集合
//获取所有的key,所有的key是一个5et集合
Set<Integer> keys = map.keySet();
//遍历key,遗过key获取val ue
//选代器可以
Iterator<Integer> it = keys.iterator();
​
​
//迭代器遍历
while(it.hasNext())(
    // 取出其中一个key
    Integer key = it.next();
    // 通过key欢取value
    String value = map.get(key); 
    System.out.println(key + "=" + value);
}
    
    
//foreach循环遍历
for(Integer key:keys){
    System.out.println(key + "=" +map.get(key));
}

第二种方式:Set<Map.Entry<K,V>>entrySet() 【效率高】

//把Map集合直接全部转换成Set集合。
// Set集合中元素的类型是:Map.Entry
Set<Map.Entry<Integer,String>> set = map.entrySet();
//遍历set集合,每一次取出一个Node
​
​
//迭代器遍历
Iterator<Map.Entry<Integer,String>> it2 = set.iterator(>;
while(it2.hasNext()){
    Map.Entry<Integer,String> node = it2.next();
    Integer key = node.getkey();
    String value = node.getValue();
    System.out.println(key + "=" + value);
}
                                                       
// foreach
for(Map.Entry<Integer,String> node : set){
    System.out.println(node.getkey() + "---y" + node.getValue());
}

2、HashMap集合

2.1 关于HashMap集合

1)HashMap集合底层是哈希表/散列表的数据结构。

2)哈希表是一个怎样的数据结构呢?

哈希表是一个数组和单向链表的结合体。
数组:在查询方面效率很高,随机增删方面效率很低。
单向链表:在随机增删方面效率较高,在查询方面效率很低。
哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。

4)HashMap集合底层的源代码:

public class HashMap{
    //HashMap底层实际上就是一个数组。(一维数组)
    Node<K,V>[] table;
    //静态的内部类HashMap.Node
    static class Node<K,V> {
        final int hash;// 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法可以转换成数组的下标
        final K key;// 存储到Map集合中的那个key
        v value;//存储到Map集合中的那个value
        Node<K,V> next;//下一个节点的内存地址。
    }
}

哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。)

5)在JDK8之后,如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树数据结构。

当红黑树上的节点数量小于6时,会重新把红思树变成单向链表数据结构。

【提高效率,二叉树的检索会再次缩小扫描范围】

6)5、对于哈希表数据结构来说:

如果o1和o2的hash值相同,一定是放到同一个单向链表上。

当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

7)HashMap允许key部分为空,但null值只能为一个


2.2 HashMap集合的key部分特点

无序,不可重复。

为什么无序?因为不一定挂到哪个单向链表上。

为什么不可重复?通过equals方法来保证HashMap集合的key不可重复。如果key重复了,value会覆盖。

放在HashMap集合key部分的元素其实就是放到HashSet集合中了。

所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。


2.3 哈希表HashMap使用不当无法发挥性能

假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。

这种情况我们成为:散列分布不均匀。

1)什么是散列分布均匀?

假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。

假设将所有的hashCode()方法返回值都设定为不一样的值,这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。

所以散列分布均匀需要重写hashCode()方法时有一定的技巧。

2)为什么哈希表的随机增删,以及查询效率都很高? 增删是在链表上完成。查询也不需要都扫描,只需要部分扫描。

重点:HashMap集合的key,会先后调用两个方法,一个方法是hashCode0,一个方法是equals0,那么这两个方法都需要重写。


2.4 map.put(k,v)实现原理

第一步:先将k,v封装到Node对象当中。

第二步:底层会调用k的hashCode()方法得出hash值,

通过哈希函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上了。

如果说下标对应的位置上有链表,此时会拿着k和链表上每一个节点中的k进行equals,

如果所有的equals方法返回都是false,那么这个新节点将果被添加到链表的末尾。

如果其中有一个equals返回了会true,那么这个节点的value将会被覆盖。


2.5 v = map.get(k)实现原理

先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,

如果这个位置上什么也没有,返回nul;

如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals,

如果所有equals方法返false,那么get方法返回null,只要其中有一个节点的k和参数k equals的时候返回true,

那么此时这个节点的value就是我们要找的value,get方法最终返回这个要找的value。

//测试HashMap集合key部分的元素特点
//Integer是key。他的euquals和hashCode都重写了
import java.util.*;
​
public class java{
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1111,"zahngsan");
        map.put(2222,"wusan");
        map.put(3333,"lisi");
        map.put(1111,"sadasd");//key重复的时候value会自动覆盖
        System.out.println(map.size());
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //遍历Map集合
        for(Map.Entry<Integer,String> entry : set){
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}
​

2.6 HashMap集合的初始化容量和扩容

HashMap集合的默认初始化容量是16,默认加载因子是0.75

这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容为原来的2倍。

重点:

HashMap 集合初始化容量必须是2的倍数,这也是官方推荐的,

这是因为达到散列均匀,为了提高HashMap集合的存取效率所必须的。

2.7 euquals和hashCode的重写

public class HashMapTest02 {
    public static void main(String[] args){
        Student s1 = new Student( name: "zhangsan");
        Student s2 = new Student( name: "zhangsan");
        
        //重写equals方法之前是false
        //System.out.println(s1.equals(s2)); // false
        
        //重写equals方法之后是true
        System.out.println(s1.equals(s2));//true(s1和s2表示相等)
        
        System.out.println("s1的hashCode=" + s1.hashCode());//284720968
        System.out.println("s2的hashCode="+s2.hashCode());//122883338
        //s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,那么往HashSet集合中放的话,
        //按说只能放进去1个。(HashSet集合特点:无序不可重复)
        Set<Student> students = new HashSet<>();
        students.add(s1);
        students.add(s2);
        System.out.println(students.size());//这个结果按说应该是1.但是结果是2.显然不符合HashSet集合存储特点,所以要重写equals和HashCode方法
    }
}

1)注意

如果一个类的equals方法重写了,那么hashCode()方法必须重写。并且equals方法返回如果是true,hashCode()方法返回的值必须一样。

equals方法返回true表示两个对象相同,在同一个单向链表上比较。那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。

所以hashCode()方法的返回值也应该相同。

2)如何重写?

直接使用IDEA工具生成,但是这两个方法需要同时生成。

3)终极结论:

放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

3、HashCode集合

Hashtable不允许key和value部分为空,会出现空指针异常

Hashtable方法都带有synchronized:线程安全的。线程安全有其它的方案,这个Hashtable对线程的处理导致效率较低,使用较少了。

HashMap和Hashtable底层都是哈希表

Hashtable的初始化容量为11,默认加载因子是0.75扩容为2倍再加一

4、Properties类

目前只需要掌properties属性类对象的相关方法即可。

Properties是一Map集合,继承Hashtable,Properties 的key和value 都是string类型。

Properties被称为属性类对象。

Properties是模程安全的。

public class PropertiesTeste1 {
    public static void main(String[] args){
        // 创建一小properties对象
        Properties pro = new Properties();
        //properties的存
        pro.setProperty("ur1", "jdbc:mysq1://localhost:3306/bjpovernode");
        pro.setProperty("driver","com.mysq1.jdbc.Driver");
        pro.setProperty("username","root");
        pro.setProperty("password", "123");
        
        // 通过key获取value
        String url = pro.getProperty("ur1");
        String driver = pro.getProperty("driver");
        String username = pro.getProperty("username");
        String password = pro.getProperty("password");
        
        System.out.println(url);
        System.out.println(driver);
        System.out.println(username);
        System.out.printIn(password);l
    }
}

5、TreeSet集合

5.1 关于TreeSet集合

1)TreeSet集合底层实际上是一个TreeMap

2)TreeMap集合底层是一个二叉树。

3)放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。

4)TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。称为:可排序集合。

5.2 排序方式:实现Comparable接口

无法对自定义类型进行排序,因为没有指定对象之间的比较原则。谁大谁小并没有说明啊。

程序运行的时候出现了这个异常:

java.long.ClossCastException:
    class com.bjpowernode.javase.collection.Person
    cannot be cast to class java.Lang.Comparable

出现这个异常的原因是:自定义类没有实现java.Lang.Comparable接口。

import java.util.TreeSet;
public class TreeSetTest04 {
    public static void main(String[] args){
        Customer c1 = new Customer( age: 32);
        Customer c2 = new Customer( age: 20);
        Customer c3 = new Customer( age: 30);
        Customer c4 = new Customer( age: 25);
        
        // 创建TreeSet集合
        TreeSet<Customer> customers = new TreeSet<>();
        //添加元素
        customers.add(c1);
        customers.add(c2);
        customers.add(c3);
        customers.add(c4);
        //遍历
        for (Customer c : customers){
            System.out.println(c);
        }
    }
}
//放在TreeSet集合中的元素需要实现java.lang.Comparable接口。
//并且实现compareTo方法oequals可以不写。
class Customer implements Comparable<Customer>{
    int age;
    public Customer(int age){
        this.age = age;
    }
    //需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!
    // k.compareTo(t.key)
    //拿着参数k和集合中的每一个k进行比较,返回值可能是>e<e=e
    //比较规则最终还是由程序员指定的:例如按照年龄升序。或者按照年龄降序。
@Override
    public int compareTo(Customer c) { // cl.compareTo(c2);
    // this是c1
    //c是c2
    //c1和c2比较的时候,就是this和c比较。
        /*int agel = this.age;
        int age2 = c.age;
        if(agel == age2){
        return 0;
        } else if(agel > age2){
            return 1;
        } else.{
            return -1;
        }*/
        //return this.age - c.age; // =0 >0 <0
        return c.age - this.age;
        
        //compareTo方法的返回值很重要:
        //返回e表示相同,value会覆盖。
        //返回>0,会继续在右子树上找。【10-9=1,1>0的说明左边这个数字比较大。所以在右子树上找。
        //返回<0,会继续在左子树上找。
        
    }
    public String toString(){
        return "Customer[age="+age+"]";
    }
}

5.3 二叉树数据结构

1)TreeSet/TreeMap是自平衡二叉树。遵循左小右大原则存放。

2)遍历二叉树的时候有三种方式:

存放是要依靠左小右大原则,所以这个存放的时候要进行比较。

前序遍历:根左右

中序遍历:左根右

后序遍历:左右根

注意: 前中后说的是“根”的位置。根在前面是前序,根在中间是中序,根在后面是后序。

3)TreeSet集合/TreeMap集合采用的是:中序遍历方式。

Iterator迭代器采用的是中序遍历方式。左根右。

4) 100 200 50 60 80 120 140 130 135 180 666 .40 55

5)采用中序遍历取出:

40 50 55 60 80 100 120 130 135 140 180 200 666

存放的过程就是排序的过程。取出来就是自动按照大小顺序排列的。

5.4 排序方式:实现Comparator比较器接口

public class TreeSetTest06 {
    public static void main(String[] args) {
        //创建TreeSet集合的时候,需要使用这个比较器。
        //TreeSet<wuGui>wuGuis=new TreeSet<>();//这样不行,没有通过构造方法传递一个比较器进去。
        
        //给构造方法传递一个比较器。
        TreeSet<WuGui> wuGuis = new TreeSet<>(new WuGuiComparator());
       
        
        wuGuis.add(new WuGui( age: 1000));
        wuGuis.add(new WuGui( age: 800));
        wuGuis.add(new WuGui( age: 810));
        
        for(WuGui wuGui : wuGuis){
            System.out.println(wuGui);
        }
    }
}
​
//乌龟
class WuGui {
    int age;
    public WuGui(int age){
        this.age = age;
    }
    @Override
    public String toString() {
        return"小乌龟["+
                "age=1" + age +
                ']';
    }
}
​
//单独在这里编写一个比较器
//比较器实现java.util.Comparator接口。(Comparable是java.lang包下的。Comparator是java.util包下的。)
class WuGuiComparator implements Comparator<WuGui> {
    @Override
    public int compare(WuGui o1, WuGui o2) {
        //指定比较规则
        //按照年龄排序
        return o1.age - o2.age;
    }
}

5.5 Comparable接口和Comparator比较器接口的选择

放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:

第一种:放在集合中的元素实现java.lang.Comparable接口。

第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。

Comparable和Comparator怎么选择呢?

当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口。

如果比较规则有多个,,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。

Comparator接口的设计符合OCP原则。

6、Collections工具类

java.util.Collection 集合接口

java.util.Collections集合工具类,方便集合的操作。

public class CollectionsTest {
    public static void main(String[] args){
        
        // ArrayList集合不是线程安全的。
        List<String> list = new ArrayList<>();
        
        //变成线程安全的
        Collections.synchronizedlist(list);
        
        //排序
        list.add("abf");
        list.add("abx");
        list.add("abc");
        list.add("abe");
        Collections.sort(list);
        for(String s : list){
            System.out.println(s);
        }
        
        List<WuGui> wuGuis = new ArrayList<>();
        wuGuis.add(new WuGui( age: 1000));
        wuGuis.add(new WuGui( age: 8000));
        Collections.sort(wuGuis);//对List集合中元素排序,需要保证List集合中的元素实现了Comparable接口
        for(WuGui wg : whGuis){
            System.out.println(wg);
        }
}
​
class WuGui implements Comparable<WuGui>{
    int age;
    public WuGui(int age){ this.age = age; }
    
    @Override
    public int compareTo(WuGui o){
        return this.age - o.age;
    }
    
    @Override
    public String |toString() {
        return "WuGui2{" +
                "age=" + age +
                '}';
    }
}
    
    //对Set集合怎么排序呢?
    Set<String> set = new HashSet<>();
    set.add("king");
    set.add("kingsoft");
    set.add("king2");
    set.add("king1");
    //将set集合转换成List集合
    List<String> myList = new ArrayList<>(set);
    Collections.sort(myList);
    for(String s :myList){
        System.out.println(s);
        //Collections.sort(l正st集合,比较器对象);
    }
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖虎不秃头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值