# Python List vs Tuple vs Set vs Dictionary: Differences, Speed and When to Use Each (Tested Examples)

**Use a** `list` **for an ordered collection you'll add to or change, a** `tuple` **for a fixed group of values that shouldn't change (like coordinates or a function returning two things), a** `set` **when you need unique items or fast "is this in here?" checks, and a** `dict` **when you want to look values up by a key. Lists and tuples keep order and allow duplicates; sets remove duplicates and have no index; dictionaries keep insertion order and map unique keys to values. In my tests on Python 3.13, checking membership in a 100,000-item set was over 20,000 times faster than in a list.**

These four are the core built-in collections in Python, and almost every program you write will use at least two of them. Picking the right one makes code shorter, faster and much harder to break. Picking the wrong one leads to classic errors like `TypeError: unhashable type: 'list'` or a script that crawls through a big file.

Every example below ran on Python 3.13.5, and the output blocks are copied straight from my terminal, including the error messages and the speed and memory measurements.

**In this guide:**

- List vs tuple vs set vs dict at a glance
- The same data in all four
- List: the everyday ordered, changeable collection
- Tuple: a fixed record that can't change
- Set: unique items and fast membership checks
- Dictionary: look things up by key
- Speed test: how fast is 'in' for each type?
- Memory test: which one uses the least?
- Hashable vs unhashable: the 'unhashable type' error
- Converting between list, tuple, set and dict
- Which one should you use?
- Frequently asked questions

## List vs tuple vs set vs dict at a glance

| Feature | list | tuple | set | dict |
| --- | --- | --- | --- | --- |
| Syntax | `[1, 2, 3]` | `(1, 2, 3)` | `{1, 2, 3}` | `{"a": 1}` |
| Ordered | Yes | Yes | No | Yes (insertion order) |
| Mutable (changeable) | Yes | No | Yes | Yes |
| Duplicates | Allowed | Allowed | Removed | Keys unique, values can repeat |
| Access by | Index `x[0]` | Index `x[0]` | No index (loop or `in`) | Key `x["a"]` |
| Membership test `in` | Slow (checks each item) | Slow (checks each item) | Fast (hash lookup) | Fast on keys |
| Can be a dict key or set item | No | Yes, if its items are hashable | No (use `frozenset`) | No |
| Empty literal | `[]` | `()` | `set()` | `{}` |

The official [Python data structures tutorial](https://docs.python.org/3/tutorial/datastructures.html) covers all four in one place if you want the reference version.

## The same data in all four

The fastest way to feel the difference is to put identical data into each one. Note that `"green"` appears twice:

```plaintext
lst = ["red", "green", "blue", "green"]
tup = ("red", "green", "blue", "green")
st  = {"red", "green", "blue", "green"}
dct = {"red": 1, "green": 2, "blue": 3, "green": 4}
print(lst); print(tup); print(st); print(dct)
print(len(lst), len(tup), len(st), len(dct))
```

```plaintext
['red', 'green', 'blue', 'green']
('red', 'green', 'blue', 'green')
{'red', 'blue', 'green'}
{'red': 1, 'green': 4, 'blue': 3}
4 4 3 3
```

Three things just happened:

- The list and tuple kept all four items, in order, duplicates included.
- The set silently dropped the second `"green"`, and the order changed.
- The dict kept one `"green"` key, but its value is `4`: a repeated key overwrites the earlier value while keeping its original position.

In real projects you'll often combine them: a list of dicts is the natural shape for table rows, and it's exactly what you pass to `csv.DictWriter` in my guide to [writing a list to CSV](https://mfaysal.com/blog/python-write-list-to-csv).

## List: the everyday ordered, changeable collection

A list is your default choice when you have a sequence of things and you'll add, remove, sort or change them. Shopping baskets, rows read from a CSV, lines from a log file, results you're collecting in a loop.

```plaintext
lst = ["red", "green", "blue", "green"]
lst.append("pink")
print(lst)
print(lst[0])
```

```plaintext
['red', 'green', 'blue', 'green', 'pink']
red
```

Lists support indexing, slicing, `sort()`, `insert()`, `pop()` and much more. They're also what list comprehensions build; my [list comprehension examples](https://mfaysal.com/blog/python-list-comprehension-examples) show the most useful patterns.

One thing to watch: assigning a list to a new name doesn't copy it. Both names point to the same list:

```plaintext
a = [1, 2]
b = a
b.append(3)
print(a)
```

```plaintext
[1, 2, 3]
```

Use `b = a.copy()` or `b = list(a)` when you want an independent copy.

## Tuple: a fixed record that can't change

A tuple looks like a list with round brackets, but once it's created you can't add, remove or replace items. That's a feature, not a limitation: it tells anyone reading your code that this group of values belongs together and shouldn't be edited.

```plaintext
tup = ("red", "green", "blue")
tup.append("pink")
```

```plaintext
AttributeError: 'tuple' object has no attribute 'append'
```

```plaintext
tup[0] = "pink"
```

```plaintext
TypeError: 'tuple' object does not support item assignment
```

Tuples shine for coordinates, RGB colours, database rows, and returning several values from a function, which you can unpack straight into variables:

```plaintext
def min_max(nums):
    return min(nums), max(nums)

lo, hi = min_max([4, 9, 1])
print(lo, hi)
```

```plaintext
1 9
```

A common beginner trap is the single-item tuple. The comma makes the tuple, not the brackets:

```plaintext
print(type(("solo")), type(("solo",)))
```

```plaintext
<class 'str'> <class 'tuple'>
```

When tuple positions start getting confusing, [collections.namedtuple](https://docs.python.org/3/library/collections.html) lets you name each field (`point.x` instead of `point[0]`) while keeping it immutable.

## Set: unique items and fast membership checks

A set stores each value only once and has no positions. That means you can't index it:

```plaintext
st = {"red", "green", "blue"}
st[0]
```

```plaintext
TypeError: 'set' object is not subscriptable
```

Sets also don't promise any order. For strings, the order can change between runs because Python randomises string hashing. I ran the same line with two different hash seeds:

```plaintext
$ PYTHONHASHSEED=1 python3 -c 'print({"red","green","blue"})'
{'red', 'green', 'blue'}
$ PYTHONHASHSEED=2 python3 -c 'print({"red","green","blue"})'
{'blue', 'red', 'green'}
```

What sets are brilliant at is maths-style comparisons between groups. Here are students taking Python and JavaScript classes:

```plaintext
python_students = {"Ayan", "Mim", "Rafi"}
js_students = {"Mim", "Tania", "Rafi"}
print(sorted(python_students & js_students))   # in both
print(sorted(python_students | js_students))   # in either
print(sorted(python_students - js_students))   # Python only
print(sorted(python_students ^ js_students))   # in exactly one
```

```plaintext
['Mim', 'Rafi']
['Ayan', 'Mim', 'Rafi', 'Tania']
['Ayan']
['Ayan', 'Tania']
```

Watch out for `{}`: it makes an empty **dict**, not an empty set. Use `set()`. And because `1 == True == 1.0` in Python, a set treats them as the same value: `{1, True, 1.0}` printed as `{1}` in my test. The [built-in types documentation](https://docs.python.org/3/library/stdtypes.html) lists every set method, including `issubset()` and `isdisjoint()`.

## Dictionary: look things up by key

A dictionary maps keys to values. Use it whenever you'd otherwise search a list to find "the item for X": a user's settings, prices by product code, word counts, JSON data.

```plaintext
counts = {}
for w in "the cat and the hat and the bat".split():
    counts[w] = counts.get(w, 0) + 1
print(counts)
```

```plaintext
{'the': 3, 'cat': 1, 'and': 2, 'hat': 1, 'bat': 1}
```

Since Python 3.7, dictionaries officially remember insertion order, so new keys go on the end:

```plaintext
d = {"b": 2, "a": 1}
d["c"] = 3
print(list(d))
```

```plaintext
['b', 'a', 'c']
```

Use `d.get(key, default)` instead of `d[key]` when a key might be missing; the square-bracket version raises `KeyError`. If you need a dictionary that can't be changed, Python 3.15 is adding a built-in [frozendict](https://mfaysal.com/blog/python-frozendict), which I covered separately.

## Speed test: how fast is 'in' for each type?

This is where the choice really matters. Checking `x in collection` on a list or tuple means Python looks at items one by one. A set or dict uses a hash table and jumps almost straight to the answer. I timed 1,000 lookups of the last item in a 100,000-item collection with the `timeit` module, taking the best of five runs:

```plaintext
import timeit
n = 100_000
setup = f"data_list=list(range({n})); data_tuple=tuple(data_list); data_set=set(data_list); data_dict=dict.fromkeys(data_list); x={n-1}"
for name in ["data_list", "data_tuple", "data_set", "data_dict"]:
    t = min(timeit.repeat(f"x in {name}", setup=setup, number=1000, repeat=5))
    print(f"{name:11} 'x in' x1000: {t*1000:8.3f} ms")
```

```plaintext
data_list   'x in' x1000:  380.818 ms
data_tuple  'x in' x1000:  337.030 ms
data_set    'x in' x1000:    0.017 ms
data_dict   'x in' x1000:    0.019 ms
```

That's roughly 0.38 seconds for the list against 0.017 milliseconds for the set. Your numbers will differ by machine, but the gap won't: list membership gets slower as the list grows, while set and dict lookups stay roughly constant on average. The [Python wiki's time complexity page](https://wiki.python.org/moin/TimeComplexity) lists this as O(n) for lists and O(1) on average for sets and dicts.

Creating them is a different story. Building a tuple literal was the quickest, and sets and dicts cost more to build because they have to hash every item:

```plaintext
list(range(1000))            x10000:   79.57 ms
tuple(range(1000))           x10000:   86.35 ms
set(range(1000))             x10000:  108.87 ms
dict.fromkeys(range(1000))   x10000:  179.60 ms
[1,2,3,4,5]                  x10000:    0.23 ms
(1,2,3,4,5)                  x10000:    0.08 ms
```

The practical rule: if you're going to check membership many times, convert to a set once and reuse it. If you only loop through the data, a list is fine.

## Memory test: which one uses the least?

I measured the container itself with [sys.getsizeof()](https://docs.python.org/3/library/sys.html) (this counts the container, not the integers inside it) on 64-bit Linux:

```plaintext
import sys
data = list(range(1000))
print("list ", sys.getsizeof(data))
print("tuple", sys.getsizeof(tuple(data)))
print("set  ", sys.getsizeof(set(data)))
print("dict ", sys.getsizeof(dict.fromkeys(data)))
print(sys.getsizeof([1,2,3]), sys.getsizeof((1,2,3)), sys.getsizeof({1,2,3}), sys.getsizeof({1:1,2:2,3:3}))
```

```plaintext
list  8056
tuple 8040
set   32984
dict  36952
88 64 216 224
```

| Type | 1,000 items (bytes) | 3 items (bytes) |
| --- | --- | --- |
| tuple | 8,040 | 64 |
| list | 8,056 | 88 |
| set | 32,984 | 216 |
| dict | 36,952 | 224 |

Tuples are the leanest, lists are close behind (they keep a little spare room for appending), and sets and dicts use about four times as much because of their hash tables. That extra memory is the price of the fast lookups above, and for most scripts it's well worth paying.

## Hashable vs unhashable: the 'unhashable type' error

Set items and dictionary keys must be **hashable**, which roughly means "can't change". Lists, sets and dicts can change, so they can't be keys:

```plaintext
{["a"]: 1}
```

```plaintext
TypeError: unhashable type: 'list'
```

The fix is usually to use a tuple instead, which works perfectly as a key, for example for map coordinates:

```plaintext
print({(51.5, -0.12): "London"})
```

```plaintext
{(51.5, -0.12): 'London'}
```

There's a catch: a tuple is only hashable if everything inside it is hashable. A tuple holding a list fails too:

```plaintext
hash((1, [2, 3]))
```

```plaintext
TypeError: unhashable type: 'list'
```

For a set you want to use as a key, there's `frozenset`. Order doesn't matter, so both of these find the same entry:

```plaintext
perms = {frozenset({"read", "write"}): "editor"}
print(perms[frozenset({"write", "read"})])
```

```plaintext
editor
```

The [Python glossary](https://docs.python.org/3/glossary.html) defines hashable precisely if you want the formal version.

## Converting between list, tuple, set and dict

You can turn any of these into another with the constructor functions. The most useful conversion is removing duplicates. `set()` does it but scrambles the order; `dict.fromkeys()` keeps the original order:

```plaintext
emails = ["a@x.com", "b@x.com", "a@x.com", "c@x.com", "b@x.com"]
print(list(dict.fromkeys(emails)))
```

```plaintext
['a@x.com', 'b@x.com', 'c@x.com']
```

I compared five ways to do this in my guide to [removing duplicates from a list](https://mfaysal.com/blog/python-remove-duplicates-from-list). Other handy conversions:

- `tuple(my_list)` to freeze a list before using it as a key.
- `list(my_set)` or `sorted(my_set)` when you need indexing or a stable order.
- `dict(zip(keys, values))` to pair two lists into a dictionary.
- `list(my_dict)` for the keys, `list(my_dict.items())` for key/value tuples.

Equality across types is strict: `[] == ()` is `False` in my test, while `tuple([1, 2]) == (1, 2)` is `True`.

## Which one should you use?

| If you need to… | Use | Example |
| --- | --- | --- |
| Keep items in order and change them | list | To-do items, CSV rows |
| Store a fixed record or return several values | tuple | (lat, lon), (min, max) |
| Remove duplicates or check "have I seen this?" | set | Visited URLs, unique tags |
| Compare two groups | set | Students in both classes |
| Look something up by name or ID | dict | Settings, prices, word counts |
| Use a collection as a dict key | tuple or frozenset | Grid positions, permission sets |
| Save memory on large fixed data | tuple | Read-only lookup tables |

When in doubt, start with a list. Switch to a set the moment you write `if x in big_list` inside a loop, and switch to a dict the moment you find yourself searching a list for a matching ID. For quick conversions and text jobs while you work, try the [free tools on mfaysal.com](https://mfaysal.com/free-tools/).

## Frequently asked questions

### What is the main difference between a list and a tuple in Python?

A list is mutable, so you can add, remove and change items after creating it. A tuple is immutable and can't be changed. Tuples use slightly less memory and can be used as dictionary keys if their contents are hashable.

### Is a set faster than a list in Python?

For membership checks like x in data, yes, by a huge margin on large collections, because sets use hashing. In my test with 100,000 items, a set was over 20,000 times faster. For looping through every item, the difference is small.

### Are Python dictionaries ordered?

Yes. Since Python 3.7, dictionaries keep keys in the order you inserted them, and this is part of the language guarantee. Sets are not ordered, and their printed order can change between runs.

### Why do I get TypeError: unhashable type: 'list'?

You tried to use a list as a dictionary key or put it in a set. Lists can change, so Python can't hash them. Convert it to a tuple with tuple(my_list), or use frozenset if order doesn't matter.

### How do I create an empty set in Python?

Use set(). Writing {} creates an empty dictionary, not a set. Once a set has items, curly braces work, for example {1, 2, 3}.

### When should I use a tuple instead of a list?

Use a tuple when the group of values is fixed and belongs together, such as coordinates, RGB colours or a function returning two results. It signals to readers that the data shouldn't change and stops accidental edits.

### Can a dictionary have duplicate keys?

No. Keys must be unique. If you write the same key twice, the later value replaces the earlier one, but the key keeps its original position. Values can repeat as often as you like.

### Which uses the least memory: list, tuple, set or dict?

Tuples use the least, lists are almost the same, and sets and dicts use around four times as much for the same number of items because of their hash tables. In my test, 1,000 integers took 8,040 bytes as a tuple and 32,984 bytes as a set.

*Last checked: 31 October 2026. Figures, settings and guidance can change, so confirm anything important with the sources linked above.*

---

*Originally published on* [*mfaysal.com*](https://mfaysal.com/blog/python-list-vs-tuple-vs-set-vs-dict)*.*

I'm learning cybersecurity on TryHackMe — follow my progress at [tryhackme.com/p/mfaysal.dev](https://tryhackme.com/p/mfaysal.dev).
