100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Arrays and Mappings

Arrays are ordered, iterable collections; mappings are hash-based key-value stores with O(1) lookup. Knowing their capabilities, limits, and gas costs is essential to writing safe contracts.

Storage & Data StructuresIntermediate9 min readJul 10, 2026
Analogies

Arrays: Ordered Collections

An array is an ordered, index-addressed collection of elements of the same type, with the first element at index 0. Solidity offers two flavors: fixed-size arrays like uint[3], whose length is fixed at compile time, and dynamic arrays like uint[], whose length can change at runtime. Arrays are the natural choice when order matters or when you need to iterate over every element — a list of proposals, a queue of participants, or a sequence of scores.

🏏

Cricket analogy: An array is the batting order 1 through 11 — an ordered, indexed lineup where position 0 opens and each slot holds one batter, exactly like array indices.

Fixed vs Dynamic Arrays

Dynamic storage arrays support push to append an element and pop to remove the last one, and expose .length. Memory arrays are different: you allocate them with new uint[](n) at a fixed length and cannot push to them. Deleting an element with delete arr[i] resets that slot to zero without shifting other elements, so removing from the middle typically means swapping with the last element and popping. Because each stored element occupies storage, large arrays are costly to grow and to iterate.

🏏

Cricket analogy: A fixed-size array is a T20 squad capped at 11, while a dynamic array is a growing net-session queue you push new batters onto and pop when done — only storage arrays allow push and pop.

solidity
contract Votes {
    uint[] public tally;        // dynamic storage array
    uint[3] public fixedScores; // fixed-size array

    function record(uint x) external {
        tally.push(x);          // push works on dynamic storage arrays
    }

    function removeLast() external {
        tally.pop();            // shrinks length by one
    }

    function sum() external view returns (uint total) {
        for (uint i = 0; i < tally.length; i++) {
            total += tally[i];  // caution: unbounded loop
        }
    }
}

Looping over an unbounded, user-growable array can push a transaction past the block gas limit, permanently bricking any function that must iterate it. Favor bounded loops, pagination, or the pull-payment pattern where users withdraw individually.

Mappings: Key-Value Lookups

A mapping is a hash-table-like structure declared as mapping(KeyType => ValueType) that provides constant-time lookup by key. Unlike arrays, mappings are not iterable: they don't store their keys, have no length, and every possible key already 'exists', returning the value type's zero default until you set it. Mappings can only live in storage — you cannot declare one in memory, pass it as an argument, or return it. Nested mappings like mapping(address => mapping(address => uint)) model two-key relationships such as ERC-20 allowances.

🏏

Cricket analogy: A mapping is the scorer's lookup from a player's name straight to their run total — instant O(1) access by key, but you can't list every name, and unknown players default to zero runs.

solidity
contract Balances {
    mapping(address => uint) public balanceOf;
    // nested mapping: owner => spender => amount
    mapping(address => mapping(address => uint)) public allowance;

    function deposit() external payable {
        balanceOf[msg.sender] += msg.value;
    }

    function approve(address spender, uint amount) external {
        allowance[msg.sender][spender] = amount;
    }
}

A mapping does not store its keys and has no .length; every key silently maps to the value type's zero default until set. That is why you cannot loop over a mapping and why deleting an entry simply resets it to the default.

Choosing Between Arrays and Mappings

Choose a mapping when you need fast lookup by a key and never have to enumerate everything — balances by address, for instance. Choose an array when order matters or you must iterate, but keep it bounded to avoid gas problems. When you need both, use the iterable-mapping pattern: store values in a mapping for O(1) access and keep a parallel array of keys so you can loop when required. This combination gives you fast reads without losing the ability to enumerate.

🏏

Cricket analogy: Use a mapping for instant player-to-score lookup, but keep a separate array of player names when you need to iterate the whole squad — the classic iterable-mapping pattern.

  • Arrays are ordered, index-accessed collections and come in fixed-size (uint[3]) and dynamic (uint[]) forms.
  • push and pop are available only on dynamic storage arrays; memory arrays have a fixed length set at creation.
  • Mappings provide O(1) key-to-value lookup but are not iterable and have no length.
  • Every possible mapping key returns the value type's default (zero) value until explicitly set.
  • Mappings can only be stored in storage and cannot be passed as function arguments or returned.
  • Iterating an unbounded array can exceed the block gas limit and lock the contract, so keep loops bounded.
  • The iterable-mapping pattern pairs a mapping with an array of keys when you need both fast lookup and enumeration.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#SolidityStudyNotes#ArraysAndMappings#Arrays#Mappings#Ordered#Collections#DataStructures#StudyNotes#SkillVeris