Java基础学习map(续)

本文详细介绍了Java中的TreeMap和HashMap数据结构及其特性。TreeMap基于红黑树,提供自动升序排序且键唯一,而HashMap基于哈希表,查询增删效率高,两者在键值对存储、扩容机制等方面有显著区别。文章还提到了Hashtable与HashMap的异同,以及如何处理HashMap的线程安全问题。此外,文中还提及了Collections工具类的常用方法和Properties类在处理配置文件中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
    TreeMap :
       存储键值对数据,key是唯一的,去重的,无序的(存放的顺序与内部真实存储的顺序不保证一致)

        底层结构 : 红黑树
        特点 : 根据key做自动升序排序(比较器)
        去重与升序排序 :  根据key比较规则做去重与排序
        新增方法 : 新增了一些与比较大小相关的方法

       注意 : Map集合下所有的实现类,去重|排序都是根据key实现的,与value无关

   练习 :
    定义TreeMap集合存储键值对数据
        键为Double类型,值为String类型,测试使用
        键为Employee类型,值为员工所在公司名字,测试使用
 */
    

public static void main(String[] args) {
            //TreeMap<String,Double> map = new TreeMap<>();  使用key的类型的默认比较器进行对键值对key做比较大小,做升序排序
            //TreeMap(Comparator<? super K> comparator) 构造一个新的空树图,根据给定的比较器排序。
            //TreeMap<Person,Double> map = new TreeMap<>(new Impl());  //根据参数传递的比较规则对key做比较去重与排序
    
            //匿名内部类
            /*TreeMap<Person,Double> map = new TreeMap<>(new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    return o1.getAge()-o2.getAge();
                }
            });*/
    
            //lambda
            TreeMap<Person,Double> map = new TreeMap<>((o1, o2)->{
                    return o1.getAge()-o2.getAge();
            });
    
            map.put(new Person("zhangsan",18),10000.0);
            map.put(new Person("wangwu",27),8888.0);
            map.put(new Person("lisi",26),9999.0);
            map.put(new Person("zhangsan",28),12000.0);
    
            System.out.println(map);
    
            //Map.Entry<K,V> ceilingEntry(K key) 返回与大于或等于给定键的最小键关联的键 - 值映射,如果没有此键,则 null 。
            //K ceilingKey(K key) 返回大于或等于给定键的 null键,如果没有这样的键,则 null 。
    
            System.out.println(map.ceilingKey(new Person("lisi",27)));
            System.out.println(map.get(map.ceilingKey(new Person("lisi",27))));
    
            //Map.Entry<K,V> floorEntry(K key) 返回与小于或等于给定键的最大键关联的键 - 值映射,如果没有此键,则 null 。
            //K floorKey(K key) 返回小于或等于给定键的最大键,如果没有这样的键,则 null 。
            System.out.println("--->"+map.floorKey(new Person("lisi",27)));
            //Map.Entry<K,V> lowerEntry(K key) 返回与严格小于给定键的最大键相关联的键 - 值映射,如果没有这样的键,则 null 。
            //K lowerKey(K key) 返回严格小于给定键的最大键,如果没有这样键,则返回 null 。
            System.out.println("--->"+map.lowerKey(new Person("lisi",27)));
    
            //Map.Entry<K,V> firstEntry() 返回与此映射中的最小键关联的键 - 值映射,如果映射为空,则 null 。
            //K firstKey() 返回此映射中当前的第一个(最低)键。
            System.out.println(map.firstKey());
            //Map.Entry<K,V> lastEntry() 返回与此映射中的最大键关联的键 - 值映射,如果映射为空,则 null 。
            //K lastKey() 返回此映射中当前的最后一个(最高)键。
        }


/*
    HashMap :
        基于哈希表的Map接口的实现。 此实现提供了所有可选的映射操作,并允许null值和null键。

        底层结构 : 哈希表(jdk8之前:数组+链表 jdk8及之后:数组+链表+红黑树)
        特点 : 查询增删效率高,去重根据键值对的key实现
        应用场景 : 适合应用在存储多个键值对数据的情况下,查询增删效率高应用场景
        新增方法 : 无新增方法
        遍历方式 : 1)values()   2)keySet()   3)entrySet()

    初始容量 :默认static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;      --> 可以通过构造器指定
    加载因子 : 默认static final float DEFAULT_LOAD_FACTOR = 0.75f;        --> 可以通过构造器指定
    扩容阈值 : threshold 存储数据的个数size > 数组的容量*加载因子
    扩容机制 : 每次扩容原容量的2倍  newCap = oldCap << 1
    哈希表中存储键值对的个数 : size

    存储过程 :
        1.调用put方法添加键值对key,value数据
        2.根据key计算hash值
            int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        3.调用putVal(hash,key,value)实现存储键值对数据
        4.执行putVal方法,第一步就判断哈希表底层的节点数组是否==null,或者底层数组的长度==0,如果是证明这是第一次添加,底层没有数组或者没有带有容量的数组,先调用resize()方法实现创建新数组
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
        5.根据key的hash值计算位桶索引int index = (n - 1) & hash,判断table[index]==null是否存在单向链表的首节点
            if ((p = tab[i = (n - 1) & hash]) == null)
        6.如果=null,证明不存在单向链表,直接创建新节点,存储这一次要添加的键值对数据,直接放在数组table[index]作为单向链表的首节点
            tab[i] = newNode(hash, key, value, null);   --> new Node<>(hash, key, value, next)
        7.如果!=null,证明存在单向链表的首节点,遍历这个链表,判断链表中每一个节点的存储的key与这一次要添加的键值对的key比较是否相等,
            if (p.hash == hash &&  ((k = p.key) == key || (key != null && key.equals(k))))
        如果相同value覆盖
            V oldValue = e.value;
            e.value = value;
            return oldValue;
        如果不相同创建新节点,挂在原单向链表的最后
            p.next = newNode(hash, key, value, null);

        8.如果以上的步骤中执行到了new Node()创建新节点放入哈希表中,数据个数+1,判断是否>扩容阈值threshold,如果满足,就调用resize方法实现扩容
            if (++size > threshold)
            resize();
    注意 :
        哈希表底层节点数组的长度为2的整数次幂
        当单向链表的节点个数>=8(static final int TREEIFY_THRESHOLD = 8;),并且同时哈希表的节点数组的容量>=64(static final int MIN_TREEIFY_CAPACITY = 64;),就把单向链表优化成红黑树
            但是如果数组的容量<64,就调用resize()实现数组的扩容
 */
  

  public class Class004_HashMap {
        public static void main(String[] args) {
            HashMap<String,Character> map = new HashMap<>();
    
            map.put("yinwei",'z');
            map.put("laoxue",'w');
            map.put("lucy",'u');
            map.put("lisa",'a');
    
            System.out.println(map);
    
            map.put("lisa",'s');
            System.out.println(map);
    
            map.remove("lisa");
            System.out.println(map);
        }
    }


/*
    Hashtable 与 HashMap之间区别 :
        共同点 :
            都是Map接口的实现类,都存储键值对的数据,底层都是哈希表,特点与应用场景类似
        异同点 :
            1.继承体系不同
                Hashtable-->父类-->java.util.Dictionary<K,V>
                HashMap -->父类-->java.util.AbstractMap<K,V>

            2.线程安全|不同问题
                HashMap : 线程不安全|不同步的,但是效率相对较高
                Hashtable : 线程安全|同步,但是效率相对较低

            3.null值的处理不同
                Hashtable  : 任何非null对象都可以用作键或值。
                HashMap : 并允许null值和null键。

            4.初始容量与扩容机制不同
                初始容量 :
                    Hashtable  : 11
                    HashMap : 16
                扩容机制
                    Hashtable  : 每次扩容原容量的2倍+1  int newCapacity = (oldCapacity << 1) + 1;
                    HashMap : 每次扩容原容量的2倍  newCap = oldCap << 1

            5.计算hash值与位桶索引index的方式不同
                    Hashtable  :
                        int hash = key.hashCode();
                        int index = (hash & 0x7FFFFFFF) % tab.length;
                    HashMap :
                        int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
                        int index = (n - 1) & hash;

 */
/*
    Collections :
        void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
        void shuffle(List) //对List容器内的元素进行随机排列
        void reverse(List) //对List容器内的元素进行逆续排列
        void fill(List, Object) //用一个特定的对象重写整个List容器
        int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象

    定义一个List集合存储Computer类型数据,要求根据电脑的价格做降序排序

    如何处理HashMap线程不安全问题 :
        1.Hashtable代替HashMap
        2.使用Collections工具类的static <K,V>Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
        3.juc高级并发编程包ConcurrentHashMap<K,V> --> 推荐

 */
  

  public static void main(String[] args) {
            List<Integer> list = Arrays.asList(3,1,5,2,4);
            System.out.println(list);
    
            //void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
            Collections.sort(list);
    
            System.out.println(list);
    
            //void shuffle(List) //对List容器内的元素进行随机排列
            Collections.shuffle(list);
    
            System.out.println(list);
    
            //void reverse(List) //对List容器内的元素进行逆续排列
            Collections.reverse(list);
    
            System.out.println(list);
    
            //int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
            //先要求升序排序
            Collections.sort(list);
            System.out.println(Collections.binarySearch(list,14)); //-6  -插入点-1
    
            ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
            map.put("haha",123);
            map.put("hehe",234);
            map.put("haha",432);
    
            System.out.println(map);
        }


/*
    Properties :
        Properties类表示一组持久的属性
        Properties可以保存到流中或从流中加载。
        属性列表中的每个键及其对应的值都是一个字符串。

    注意 :
        应用场景 : 从properties格式的配置文件中读取键值对数据
            步骤 :
                1.定义xx.properties的配置文件
                2.构建Properties类型的对象
                3.调用load方法实现从指定流中加载
                4.调用getProperty方法根据key获取value从文件中加载数据
 */

    public static void main(String[] args) throws IOException {
            Properties pro = new Properties();
            pro.setProperty("嘻嘻","123");
            pro.setProperty("哈哈","456");
    
            System.out.println(pro);
    
            //1)通过Properties对象从数据源文件中加载键值对数据(属性数据)
            //把pro对象与流建立联系,从流中加载数据
            //void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。
            //pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("classname.properties"));
    
            //根据key获取value,根据属性名获取属性值
            //System.out.println(pro.getProperty("xixi"));;
            //System.out.println(pro.getProperty("haha"));;
            //System.out.println(pro.getProperty("hehe"));;
    
            //2)把Properties存储的键值对数据写出到文件中-->了解
            //pro对象与流建立联系,把键值对数据写出到目的地properties文件中
            //void store(OutputStream out, String comments)
            BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("src/dest.properties"));
            pro.store(os,"注释xixihaha");
            os.flush();
            os.close();
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值