Java小白一文讲清Java中集合相关的知识点(七)

LinkedHashSet

  • LinkedHashSet是HashSet的子类

  • LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表

    而在之前讲的HashSet中的链表是单向的哈,注意区分!

  • LinkedHashSet根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,

    这使得元素看起来是以插入的顺序保存的

  • LinkedHashSet不允许添加重复元素

  • 第一次向其中添加元素时,直接将数组table扩容到16,存放的结点类型是LinkedHashMap$Entry

    数组table是HashMap$Node[ ],但是存放的数据/元素

    是LinkedHashMap$Entry类型的,这其实就是数组的多态的体现;

        static class Entry<K,V> extends HashMap.Node<K,V> {
            //此中的before和after是进行双向链表的连接的
            //其继承关系是在内部类完成的
            Entry<K,V> before, after;
            Entry(int hash, K key, V value, Node<K,V> next) {
                super(hash, key, value, next);
            }
        }
    
    

在这里插入图片描述

补充:

  • 当编写一个LinkedHashSet的应用实例时,比如让你创建一个Car类,然后创建好几辆车,有各自的名字和价格,要求如果名字和价格相等的话,无法加入到LinkedHashSet中去,这时候就要重写equals和hashCode方法,那么,有个问题,我如果只写二者其一,可不可以呢?即只保留equals或者hashCode; 答案是:不行,因为LinkedHashSet底层是HashMap,所以实现去重的源码部分是一样的,必须要equals和hashCode二者的值都相等,才符合去重条件,才会拒绝第二个,第三个equals和hashCode值都相等的Car对象的加入

        //注意,equals和hashCode两个方法少一个都无法实现去重,
        //因为其底层判断的时候就是要二者都相同才符合去重条件
        /**
         if (p.hash == hash &&
         ((k = p.key) == key || (key != null && key.equals(k))))
         e = p;
         */
    

Map接口和常用方法

Map接口

  • Set和Map其实二者都是K-V存储,但是不同于Map,Set中的V采用的是PRESENT,所以我们只能在Set中自定义存储K,V会由底层自动实现,而Map中,K、V都是可以自定义存放的,so 这也是为什么说Set其实就是Map的原因;
  • Map与Collection并列存在,即二者是两大类,是同级关系,Map用于保存具有映射关系的数据:key-value
  • Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
  • Map中的key不允许重复,原因和HashSet一样
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("1","kerwin");
        map.put("2","囿于pain");
        map.put("1","jas");//当有相同K时,就会等价替换
        System.out.println("map ="+map);
        //{1=jas, 2=囿于pain}

        //再加个重复的value,试试
        map.put("3","jas");
        System.out.println("map ="+map);
//      map ={1=jas, 2=囿于pain, 3=jas}

    }
  • Map中的value可以重复
  • Map的key可以为null,value也可以为null,注意key为null时,只能有一个,value为null时,可以有多个
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("1","kerwin");
        map.put("2","囿于pain");
        map.put("1","jas");//当有相同K时,就会等价替换
        System.out.println("map ="+map);
        //{1=jas, 2=囿于pain}

        //再加个重复的value,试试
        map.put("3","jas");
        System.out.println("map ="+map);
//      map ={1=jas, 2=囿于pain, 3=jas}

        map.put(null,null);
        System.out.println(map);
//     {null=null, 1=jas, 2=囿于pain, 3=jas}

        //key只能有一个是null,重复加入,会发生替换
        //value可以有多个是null
        map.put("4",null);
        map.put("5",null);
        map.put(null,"fulture");
        System.out.println(map);
//      {null=fulture, 1=jas, 2=囿于pain, 3=jas, 4=null, 5=null}

    }
  • 常用String类作为Map的key
  • key和value之间存在单向一对一的关系,即通过指定的key总能找到对应的value
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("1","kerwin");
        map.put("2","囿于pain");
        map.put("1","jas");//当有相同K时,就会等价替换
        System.out.println("map ="+map);
        //{1=jas, 2=囿于pain}

        //再加个重复的value,试试
        map.put("3","jas");
        System.out.println("map ="+map);
//      map ={1=jas, 2=囿于pain, 3=jas}

        map.put(null,null);
        System.out.println(map);
//     {null=null, 1=jas, 2=囿于pain, 3=jas}

        //key只能有一个是null,重复加入,会发生替换
        //value可以有多个是null
        map.put("4",null);
        map.put("5",null);
        map.put(null,"fulture");
        System.out.println(map);
//      {null=fulture, 1=jas, 2=囿于pain, 3=jas, 4=null, 5=null}

        //通过传入的key,由于key是不可重复的,故会返回对应的value
        System.out.println(map.get("2"));//囿于pain
        System.out.println(map.get(null));//fulture

    }

  • Map存放数据的key-value示意图,一对k-v是放在一个Node上的,又因为Node实现了Entry接口,有些书上也说一对k-v就是一个Entry

在这里插入图片描述

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
    ....
}
  • K-V 为了方便程序员的遍历,还会创建EntrySet 集合,该集合存放的元素的类型是Entry, 而一个Entry对象就有k,v ,即Entry由Entr组成,而每个Entry包含一对K-V, EntrySet<Entry<K,V>> , 源码如下:
transient Set<Map.Entry<K,V>> entrySet;
  • entrySet中,定义的类型是Map.Entry ,但是实际上存放的还是HashMap$Node,这是因为Node实现了Map.Entry,即接口的多态,接口引用指向了实现了接口的类,源码即:

    static class Node<K,V> implements Map.Entry<K,V> {
    
  • 当把HashMap$Node 对象存放到 entrySet 就方便我们的遍历,因为 Map.Entry接口提供了重要的方法

    K getKey() V getValue()

    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("1","kerwin");
        map.put("2","jackon");
        Set set = map.entrySet();
        System.out.println(set.getClass());
        //HashMap$EntrySet

        for(Object obj : set){
//            System.out.println(obj.getClass());
            //为了从HashMap$Node 中取出k-v
            //1.先做一个向下转型
            Map.Entry entry = (Map.Entry)obj;
            System.out.println(entry.getKey()+" - "+entry.getValue());
            //  1 - kerwin
            //	2 - jackon
        }
        
    }

  • HashMap中的table表采用数组+链表+红黑树来维护,数组中存放Node类型数据,但是为了方便管理,其在底层也做了一层控制,将每个Node封装成一个Entry,然后将一个个Entry,放到EntrySet集合中去,这样实现的主要目的是方便管理;除此之外,还提供了一个KeySet集合,其中单独地封装了一个个的Node中的key,形成KeySet集合,同理,将所有的value抽取出来,封装成了一个Collection集合,源码如下:
public static void main(String[] args) {
        Map map = new HashMap();
        map.put("1","kerwin");
		Set set1 = map.keySet();
    	System.out.println(set1.getClass());//HashMap$KeySet  是HashMap的内部类,负责管理键
		Collection values = map.values();
		System.out.println(values.getClass());//HashMap$Values  是HashMap的内部类,负责管理值
        
     	//遍历下set1  和  values
        Iterator iterator = set1.iterator();
        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
        //输出
//        1
//        2

        Iterator iterator2 = values.iterator();
        while (iterator2.hasNext()) {
            Object obj = iterator2.next();
            System.out.println(obj);
        }
        //输出
//        kerwin
//        jackon
}

常用方法

  • put添加
  • remove根据键删除映射关系
  • get根据键获取值
  • size获取元素个数
  • isEmpty判断个数是否为0
  • clear清除
  • containsKey查找键是否存在
@SuppressWarnings({"all"})
public class Journey {
    public static void main(String[] args) {
        Map map = new HashMap();
        //添加
        map.put("1","kerwin");
        map.put("2","jackon");
        map.put(true,123);
        //删除
        map.remove(true);

        System.out.println(map);
        //{1=kerwin, 2=jackon}

        System.out.println(map.get("2"));//jackon

        map.put(null,123);
        map.put(false,null);
        //获取元素个数
        System.out.println(map.size());//4
        //判断是否为空
        System.out.println(map.isEmpty());//false
        //查找键是否存在
        System.out.println(map.containsKey(false));//true

        //清除
        map.clear();
        System.out.println(map);//{}
    }
}

Map接口遍历方法

  • containsKey 查找键是否存在
  • keySet 获取所有的键
  • entrySet 获取所有的关系
  • values 获取所有的值
    public static void main(String[] args) {
        Map map = new HashMap();
        //添加
        map.put("1", "kerwin");
        map.put("2", "jackon");
        map.put(true, 123);
        map.put(null, 123);
        map.put(false, null);

        //查找键是否存在
        System.out.println(map.containsKey(false));//true

        //直接打印keySet  values   entrySet
        System.out.println(map.keySet());
        //[null, 1, 2, false, true]
        System.out.println(map.entrySet());
        //[null=123, 1=kerwin, 2=jackon, false=null, true=123]
        System.out.println(map.values());
        //[123, kerwin, jackon, null, 123]


        System.out.println("===================================");
        //迭代遍历打印
        Set keys = map.keySet();
        Iterator iterator = keys.iterator();
        System.out.println("keySet:");
        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }

        Collection values = map.values();
        Iterator iterator1 = values.iterator();
        System.out.println("values:");
        while (iterator1.hasNext()) {
            Object obj = iterator1.next();
            System.out.println(obj);
        }

        Set entry = map.entrySet();
        Iterator iterator2 = entry.iterator();
        System.out.println("entrySet:");
        while (iterator2.hasNext()) {
            Object obj = iterator2.next();
            System.out.println(obj);
        }
        /**
         * ===================================
         * keySet:
         * null
         * 1
         * 2
         * false
         * true
         * values:
         * 123
         * kerwin
         * jackon
         * null
         * 123
         * entrySet:
         * null=123
         * 1=kerwin
         * 2=jackon
         * false=null
         * true=123
         */
    }

练习

在这里插入图片描述

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({"all"})
public class CodingDemo {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        Employee emp1 = new Employee("小A", 20000, 1);
        Employee emp2 = new Employee("小B", 10000, 2);
        Employee emp3 = new Employee("小C", 15000, 6);
        Employee emp4 = new Employee("小D", 19000, 10);
        map.put(emp1.getId(),emp1);
        map.put(emp2.getId(),emp2);
        map.put(emp3.getId(),emp3);
        map.put(emp4.getId(),emp4);


        //实现遍历--keySet--增强for
        Set keySet = map.keySet();
        System.out.println("==========第一种遍历方式===========");
        for (Object key : keySet) {
            //先获取value
            Object o = map.get(key);
            Employee emp = (Employee)o;
            if(emp.getSal()>18000){
                System.out.println(emp);
            }
        }


        //实现遍历--EntrySet---迭代器
        Set entrySet = map.entrySet();
        System.out.println("===========第二种遍历方式==============");
        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Object obj  = iterator.next();
            Map.Entry entry = (Map.Entry)obj;
            Employee emp = (Employee) entry.getValue();
            if(emp.getSal()>18000){
                System.out.println(emp);
            }
        }

    }
}
@SuppressWarnings({"all"})
class Employee{
    private String name;
    private double sal;
    private int id;

    public Employee(String name, double sal, int id) {
        this.name = name;
        this.sal = sal;
        this.id = id;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", id=" + id +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

HashMap小结

  • Map接口的常用类:HashMap、Hashtable、Properities
  • HashMap是Map接口使用频率最高的实现类
  • HashMap是以key-value 对的方式(HashMap$Node类型)来存储数据
  • key不能重复,但是值可以重复,允许使用null键和null值
  • 如果添加相同的key,则会覆盖原来的key-value,等同于修改(key不会替换,value会替换)
  • 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的
  • HashMap没有实现同步,因此是线程不安全的,方法上没有做同步互斥操作,没有synchronized关键字修饰
  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值