- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
How a Hash Map Works (and Why Your PHP Array Is Secretly One)
About Post
You have a list of ten thousand IDs and you want to know whether 48213 is in it. You can check every item one by one. Or you can jump straight to the answer, almost instantly, whether the list has ten thousand items or ten million.
That second option sounds like cheating. It's a hash map, and you use one every day: every PHP array, every JavaScript object and Map, every Python dictionary, every cache key. Understanding how it works explains why some code is mysteriously fast, why other code is mysteriously slow, and one of the stranger security bugs in web history.
The coat check
Picture a coat check with a hundred numbered hooks. You hand over your coat and the attendant doesn't put it on the next free hook. Instead, they take your name, run it through a little rule (say, add up the letters and take the last two digits) and get a hook number. Your coat goes on hook 37.
When you come back, they don't search all hundred hooks. They run your name through the same rule, get 37 again, and walk straight to it.
That's the whole idea:
- The hooks are an array of slots, usually called buckets.
- The rule is a hash function: it turns a key into a number, quickly, and always gives the same number for the same key.
- The number, reduced to the size of the array (with a modulo, for example), is the bucket index.
Looking something up doesn't depend on how many items there are. Hash the key, jump to the bucket, done.
A tiny hash map, from scratch
Here's a toy version in JavaScript. Real implementations are far more optimised, but the shape is the same:
class TinyHashMap {
constructor(size = 8) {
this.buckets = Array.from({ length: size }, () => []);
}
index(key) {
let h = 0;
for (const ch of String(key)) h = (h * 31 + ch.charCodeAt(0)) >>> 0;
return h % this.buckets.length;
}
set(key, value) {
const bucket = this.buckets[this.index(key)];
const entry = bucket.find(([k]) => k === key);
if (entry) entry[1] = value;
else bucket.push([key, value]);
}
get(key) {
return this.buckets[this.index(key)].find(([k]) => k === key)?.[1];
}
}
Notice that each bucket is a small list, not a single slot. That's because of the one problem every hash map has to solve.
Collisions: two coats, one hook
With a limited number of buckets and an unlimited number of possible keys, two different keys will sometimes land in the same bucket. That's a collision, and it's not a bug. It's guaranteed.
There are two classic ways to handle it:
- Chaining: each bucket holds a short list, and colliding entries share it. Lookup jumps to the bucket, then checks the few items in it. That's what the toy above does.
- Open addressing: if the bucket is taken, try the next one according to some rule until you find a free slot. Python's dictionaries work this way.
Either way, a lookup still has to compare the actual keys at the end, because the hash only tells you where to look, not that you found the right thing.
Why it's "O(1) on average"
Lookups are fast only while buckets stay short. If you keep adding items to a fixed number of buckets, the lists grow and every lookup slows down. So real hash maps track their load factor (items divided by buckets), and when it gets too high, they resize: allocate a bigger bucket array, typically double the size, and re-insert everything.
Resizing is slow when it happens, but it happens rarely, and doubling means the cost spreads out across all the inserts that came before. That's what "amortised O(1)" means: occasionally expensive, cheap on average.
And the worst case? If every key lands in the same bucket, the hash map becomes a slow list, and each lookup is O(n). With a decent hash function and normal data, that essentially never happens by accident.
When it happens on purpose
By accident, no. On purpose, yes. In 2011, security researchers showed that many web platforms used predictable hash functions for request parameters. An attacker could craft a single POST request with thousands of keys that all collided, and the server would spend a long time building one hopelessly slow hash table. Small request, huge CPU cost.
The fixes varied. Many languages moved to hash functions with a random secret seed, so attackers can't predict collisions. PHP's answer at the time was the max_input_vars setting, which caps how many input variables a request can create. That's why that setting exists in your php.ini.
Your PHP array is a hash map
PHP has one array type, and under the hood it's an ordered hash table. Lists, dictionaries, and that associative array your config file returns are all the same structure (PHP has an optimised "packed" mode for plain lists, but it's the same type from your point of view). A few practical consequences:
Key lookups are fast; value searches are not. isset($map[$key]) hashes and jumps. in_array($value, $list) walks the whole array. Inside a loop, that difference is the gap between instant and painfully slow:
// O(n) every time: scans the whole list
in_array($id, $blockedIds);
// O(1) on average: flip once, then hash lookups
$blocked = array_flip($blockedIds);
isset($blocked[$id]);
If you check membership inside a loop over thousands of rows, flipping the array first is one of the cheapest performance wins in PHP.
Keys get normalised. A string key that looks like a plain decimal integer, like "42", is stored as the integer 42. But "042" stays a string. So $a["42"] and $a[42] are the same element, which is usually convenient and occasionally very confusing when IDs come in as strings.
Order is preserved. Unlike a textbook hash map, PHP arrays remember insertion order, which is why foreach gives you items in the order you added them.
And in JavaScript
Plain objects work as hash maps, but their keys are always strings (or symbols), and they come with inherited properties. Map accepts any key type, keeps insertion order, has a size, and is designed for frequent adds and deletes. For a real lookup table, Map is usually the better choice; for a fixed shape like a config object, a plain object is fine.
The practical rule: if you find yourself searching a list for a value over and over, turn it into a hash map keyed by that value once. Searching is O(n). Looking up is O(1).
The short version
- A hash function turns a key into a bucket number; lookups jump straight there.
- Collisions are normal; chaining or open addressing handles them.
- Resizing keeps buckets short, so lookups stay O(1) on average.
- Predictable hashes can be attacked; that's why seeds and
max_input_varsexist. - In PHP, prefer
isseton keys overin_arrayon values in hot loops.
Where did a hash map last save you, or where would one have? I'd love to hear your best in_array-in-a-loop story.

Be first to comment it...