HashMap 与 TreeMap

集合中Map

一、说明HashMap、LinkedHashMap、Hashtable、TreeMap区别

Map : 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。

在这里插入图片描述

  • HashMap:非线程安全的,JDK1.8 之前 HashMap 由数组+链表组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突存在的(“拉链法”)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阀值(默认8)(将链表转换成红黑树前会判断,如果当前数组的长度小于64,那么会先选择数组扩容,而不是转换为红黑树),将链表转换为红黑树,以减少搜索时间。
  • LinkedHashMap:继承 HashMap,底层仍然是基于拉链式散列结构(数组和链表或红黑树)。LinkedHashMap 增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。通过对链表进行相应的操作,实现了访问顺序相关逻辑,
  • Hashtable:线程安全的,数组+链表组成,数组是主体,链表为了解决哈希冲突。继承Dictionary类。
  • TreeMap:红黑树(自平衡的排序二叉树)
keyvalue
HashMap只能一个key为null可以多个值为null
Hashtable不能为null不能为null
TreeMap不能为null不能为null
二、HashMap 和 TreeMap 区别

TreeMap 和 HashMap 都继承自 AbstractMap,但是需要注意的是 TreeMap 它还实现了 NavigableMap 接口和 SortedMap 接口。

在这里插入图片描述

实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力。

实现 SortedMap 接口让 TreeMap 有了对集合中的元素根据键排序的能力。默认是按 key 的升序排序。

public class Person {
    private Integer age;

    public Person(Integer age) {
        this.age = age;
    }

    public Integer getAge() {
        return age;
    }

    public static void main(String[] args) {
        TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() {
            @Override
            public int compare(Person person1, Person person2) {
                int num = person1.getAge() - person2.getAge();
                return Integer.compare(num, 0);
            }
        });
        treeMap.put(new Person(4), "person1");
        treeMap.put(new Person(28), "person2");
        treeMap.put(new Person(35), "person3");
        treeMap.put(new Person(30), "person4");
        treeMap.entrySet().stream().forEach(personStringEntry -> {
            System.out.println(personStringEntry.getValue());
        });
    }
}

输出:

person1
person2
person4
person3
三、如何选用集合

主要根据集合的特点选用,需要根据键值对获取元素值选用 Map 接口下集合,需要排序时选择 TreeMap,不需要排序时就选择 HashMap,需要保证线程安全就选用 ConcurrentHashMap

四、应用场景

1、社保付款中服务费计算,不同服务费需要考虑不同的结算地区,ESC中系统数据源存放的地区按照省-市-区的三个字段存放,在EIS系统中存放的结算地区都是按照最后一级code存放

地区ESC(小爱)EIS(自研)
江苏省320000320000
江苏省苏州市320000-320500320500
江苏省苏州市工业园区320000-320500-320510320510

2、EIS系统服务设置

服务费名称结算地区
1-固定单价-10江苏省
2-打包价-100江苏省苏州市
3-阶梯单价-20江苏省苏州市工业园区

3、社保付款服务计算需要按照地区匹配计算,如果统一按照地区匹配计算,结算地区江苏省苏州市会把江苏省苏州市工业园区多计算了。实际处理中将接口获取到的服务规则,转换成以城市的值大小排序的TreeMap。可以实现优先计算区级别->市级别->省级别服务费计算。

// 将服务费规则转换成以城市编码为key的Map集合 
// 原来一个服务费对应多个城市,转换成一个城市对应多个服务费
private static TreeMap<Long, List<SupplierContractServiceRuleVO>> buildCityRuleMap(List<SupplierContractServiceRuleVO> rules) {
    // 地区编码按照从大到小排序,编码大的优先放在前面(不同省份的同级别比较不影响)
    TreeMap<Long, List<SupplierContractServiceRuleVO>> map = new TreeMap<>(new Comparator<Long>() {
        @Override
        public int compare(Long o1, Long o2) {
            return (int) (o2 - o1);
        }
    });
    if (CollUtil.isEmpty(rules)) {
        return map;
    }
    for (SupplierContractServiceRuleVO rule : rules) {
        if (CollUtil.isEmpty(rule.getCities())) {
            /// 没有设置城市的服务费规则,默认城市编码0,排序在最后面
            List<SupplierContractServiceRuleVO> list = map.getOrDefault(0L, new ArrayList<>());
            list.add(rule);
            map.put(0L, list);
        } else {
            for (String city : rule.getCities()) {
                List<SupplierContractServiceRuleVO> list = map.getOrDefault(Long.valueOf(city), new ArrayList<>());
                list.add(rule);
                map.put(Long.valueOf(city), list);
            }
        }
    }
    return map;
}

4、数据匹配,按照转换后的城市为key的服务费TreeMap匹配对应的社保缴纳数据,计算对应的服务值,并移除已经计算过的数据。

for (Map.Entry<Long, List<SupplierContractServiceRuleVO>> entry : cityRuleMap.entrySet()) {
    if (0L == entry.getKey()) {
        continue;
    }
    BigDecimal temp = BigDecimal.ZERO;
    Map<Integer, List<SupplierContractServiceRuleVO>> rulesMap2 = entry.getValue().stream()
            .collect(Collectors.groupingBy(SupplierContractServiceRuleVO::getChargeType));
    SupplierContractServiceRuleVO rule = entry.getValue().get(0);
    if (CollUtil.isNotEmpty(rulesMap2.get(0))) {
        List<SocialRequestData> filterNormalList = filterMatchCityData(entry.getKey().toString(), rule.getServiceScope(), normalList);
        Set<String> normalIdNumSet = filterNormalList.stream().map(SocialRequestData::getIdNum).collect(Collectors.toSet());
        temp = temp.add(reduceAmount(rule.getRule(), rulesMap2.get(0), normalIdNumSet.size()));
        // 这边移除匹配上城市的数据
        normalList.removeIf(next -> normalIdNumSet.contains(next.getIdNum()));
    }
    if (CollUtil.isNotEmpty(rulesMap2.get(1))) {
        List<SocialRequestData> filterSpecList = filterMatchCityData(entry.getKey().toString(), rule.getServiceScope(), specList);
        Set<String> specIdNumSet = filterSpecList.stream().map(SocialRequestData::getIdNum).collect(Collectors.toSet());
        temp = temp.add(reduceAmount(rule.getRule(), rulesMap2.get(1), specIdNumSet.size()));
        // 这边移除匹配上城市的数据
        specList.removeIf(next -> specIdNumSet.contains(next.getIdNum()));
    }
    // 最低收费标准 与 (正常收费+补缴收费)比较
    temp = temp.max(rule.getBottomPrice());
    serviceAmount.set(serviceAmount.get().add(temp));
}
五、思考

在想到这种方式之前,打算将所有城市放进优先队列(PriorityQueue),但在计算数据服务费时,需要筛选出对应的服务费规则,还要筛选对应的社保缴纳数据,同时还需要对优先队列进行出队操作,代码量较多,逻辑复杂程度比使用TreeMap复杂,出问题排查也比较困难。

六、引用部分

Java集合常见面试题总结(上)
Java集合常见面试题总结(下)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
HashMapTreeMap都是Java中的Map接口的实现类。它们都可以用来存储键值对,并提供了快速的查找和检索功能。然而,它们在一些方面有一些区别。 首先,HashMap是基于哈希表的实现,它使用键的哈希值来存储和检索元素。它的插入、删除和定位元素的性能都非常高效,时间复杂度为O(1)。HashMap不保证元素的顺序,在遍历时无法保证元素的顺序。如果你只关心存储和检索的性能,并不关心元素的顺序,HashMap是更好的选择。 而TreeMap则是基于红黑树的实现,它能够对键进行排序并保持有序。TreeMap中的元素是按照键的自然顺序进行排序的,或者可以通过传入一个Comparator对象来指定排序规则。因此,如果你需要对一个有序的键集合进行遍历,或者需要得到一个有序的结果,TreeMap是更好的选择。但是由于它要维护树结构,插入、删除和定位元素的性能相对较低,时间复杂度为O(log n)。 总之,如果你需要快速的插入、删除和定位元素,并不关心元素的顺序,HashMap是最好的选择。而如果你需要对有序的键集合进行遍历,或者需要得到一个有序的结果,TreeMap是更好的选择。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [7月5日Java学习(HashMap)](https://blog.csdn.net/m0_51947717/article/details/125612608)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值