HashMap的应用,CF650A

本文介绍了一个使用Java实现的点坐标统计程序。该程序接收一组点坐标数据,通过使用HashMap来统计相同横纵坐标的出现次数,并计算出仅横坐标或纵坐标相同的点对数量。文章中的代码展示了如何有效地组织数据并进行统计,适用于需要处理大量坐标数据的场景。

题目输入一个n,接下来有n组数据,每组数据是n个point(x,y);输出xi == xj || yi == yj的数量,注意要减去xi == xj && yi == yj的数量

代码如下,写的复杂的原因是因为想练习一下HashMap

import java.awt.Point;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Scanner;
import java.util.*;

public class Main {

    public static void main(String[] args) {
    
       Scanner input = new Scanner(System.in);
       HashMap<Integer,Long> xs = new HashMap<>();
       HashMap<Integer,Long> ys = new HashMap<>();
       HashMap<Point,Long> locs = new HashMap<>();
       int n = input.nextInt();
       for(int i = 0;i < n;i++){
    	   int x = input.nextInt();
    	   int y = input.nextInt();
    	   Point p = new Point(x, y);
    	   Long cx = (long) 0,cy = (long) 0,cp = (long) 0;
    	   if(xs.containsKey(x)){
    		   cx = xs.get(x);
    	   }
    	   if(ys.containsKey(y)){
    		   cy = ys.get(y);
    	   }
    	   if(locs.containsKey(p)){
    		   cp = locs.get(p);
    	   }
    	   cx++;
    	   cy++;
    	   cp++;
    	   xs.put(x, cx);
    	   ys.put(y, cy);
    	   locs.put(p, cp);
       }
       Iterator ite =  xs.keySet().iterator();
       long ans = 0;
       while(ite.hasNext()){
    	   int l = (int) ite.next();
    	   ans += (xs.get(l)-1) * xs.get(l) / 2;
       }
       
       ite = ys.keySet().iterator();
       while(ite.hasNext()){
    	   int l = (int) ite.next();
    	   ans += (ys.get(l)-1) * ys.get(l) /2;
       }
       
       ite = locs.keySet().iterator();
       while(ite.hasNext()){
    	   Point l = (Point)ite.next();
    	   ans -= locs.get(l) *(locs.get(l)-1) / 2 ;
       }
       System.out.println(ans);
    }
}


<think>嗯,用户想了解HashMap应用场景和实现原理。首先,我需要回忆一下HashMap的基本结构。记得HashMap是基于哈希表实现的,主干是一个Entry数组,每个Entry包含键值对,引用[2]提到过这个。哈希表通过计算键的哈希值来快速定位存储位置,这样可以实现高效的查找、插入和删除操作。 接下来是应用场景。用户可能需要知道在什么情况下使用HashMap比较合适。比如缓存数据,因为快速存取;统计频率,因为键的唯一性;还有去重操作。这些场景都需要快速的查找能力,而HashMap的O(1)时间复杂度很合适。 然后是实现原理部分。哈希函数的设计很重要,引用[5]提到位运算比模运算高效,所以HashMap中可能用到了位运算来计算索引。处理哈希冲突的方法,比如链地址法,当多个键哈希到同一个位置时,用链表或红黑树存储。扩容机制方面,负载因子默认0.75,超过阈值会扩容,引用[3]有说明。扩容后要重新计算哈希,可能影响性能。 还要考虑线程安全的问题,HashMap不是线程安全的,可以用ConcurrentHashMap替代。源码分析部分,可以提到put方法的流程,包括计算哈希、处理冲突、扩容等步骤。引用[4]提到重写equals和hashCode的必要性,这点很重要,否则会影响HashMap的正确性。 用户可能还想知道为什么负载因子是0.75,或者哈希冲突的解决方法除了链表还有什么。不过根据问题,主要需要覆盖应用场景和实现原理,可能还需要比较其他数据结构,比如HashTable或TreeMap的区别。 总结一下,回答需要分两部分:应用场景和实现原理,结合引用中的信息,确保准确引用来源。同时,生成的相关问题要引导用户进一步学习,比如优化方法或线程安全的替代方案。</think>### HashMap应用场景及实现原理分步解析 --- #### 一、HashMap应用场景 1. **快速存取键值对** HashMap通过哈希函数直接计算键的存储位置,时间复杂度接近$O(1)$,适合高频次查询场景。例如: - 缓存数据(如用户会话信息) - 统计词频(如统计文章中单词出现次数) - 数据去重(利用键的唯一性) 2. **无需有序存储的场景** HashMap不保证遍历顺序与插入顺序一致,适合对顺序无要求的场景,例如: - 数据库查询结果临时存储 - 配置文件解析为键值对集合 3. **替代简单关系型数据库查询** 对于小规模数据,可用HashMap实现类似“内存数据库”的功能,例如: ```java Map<Integer, User> userCache = new HashMap<>(); userCache.put(userId, userObject); // 快速通过userId获取用户对象 ``` --- #### 二、HashMap的实现原理 ##### 1. **数据结构** - **主干结构**:Entry数组(JDK8后改为Node数组),每个节点存储键值对[^2] ```java static class Node<K,V> implements Map.Entry<K,V> { final int hash; // 哈希值 final K key; V value; Node<K,V> next; // 链表指针 } ``` - **哈希冲突解决**:链地址法(链表) + 红黑树(JDK8优化) - 当链表长度超过8时,转为红黑树(时间复杂度从$O(n)$降为$O(\log n)$) ##### 2. **核心机制** - **哈希函数** 通过键的`hashCode()`计算哈希值,再通过位运算确定数组下标: $$ \text{index} = (n - 1) \& \text{hash} $$ 其中$n$为数组长度(始终为2的幂),位运算(&)替代模运算提升效率[^5] - **扩容机制** - 默认负载因子(loadFactor)为0.75[^3] - 扩容条件:元素数量 > 数组长度 × loadFactor - 扩容操作:新建2倍大小的数组,重新哈希所有元素 ##### 3. **线程安全问题** - **非线程安全**:多线程并发修改可能导致链表成环或数据丢失 - **替代方案**:使用`ConcurrentHashMap`或`Collections.synchronizedMap()` --- #### 三、源码级实现分析(以JDK8为例) 1. **put方法流程** ```java public V put(K key, V value) { // 1. 计算哈希值 int hash = hash(key); // 2. 计算数组下标 int i = (n - 1) & hash; // 3. 处理哈希冲突(链表/红黑树) if (table[i] == null) { table[i] = newNode(hash, key, value, null); } else { // ...遍历链表或树 } // 4. 检查扩容 if (++size > threshold) resize(); } ``` 2. **重写equals和hashCode** 必须同时重写这两个方法以保证逻辑一致性[^4],例如: ```java public class Key { private int id; @Override public int hashCode() { return Objects.hash(id); } @Override public boolean equals(Object o) { /* 比较id字段 */ } } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值