Collections are very similar across the different programming languages. They involve the same data structures, the same functions and essentially look the same in every language.
If you are learning collections, just know you are learning it for all languages so be encouraged about learning these data types.
In every language they are a library of data structures used in every day programming. They are called collections because each data structure is a collection of objects. These collections manipulate memory in a certain way. They are implementations of stacks, queues, maps, lists, etc, given to you for your convenience.
The way you manipulate memory is structured by its collection type. A stack for example is LIFO (Last-In-First-Out). Let’s look at this in Java:
import java.util.Stack;
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(stack.pop()); // Outputs 3
The key part, stack already exists in the collections library. java.util.* has all the collections inside it. You do not have to implement any collections. They come with the functions and inner data structures. They are meant to make your problem solving easier.
Let’s look at one more, Maps:
import java.util.HashMap;
import java.util.Map;
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 30);
map.put("Bob", 25);
map.put("Charlie", 35);
System.out.println("Age of Bob: " + map.get("Bob")); // Outputs 25
You place three numbers in three different keys from String to Integer. You do not implement the data structure, you just import it.
In the background these collections are wrappers of primitives. If you look inside the data structure you will see an int[] array, or a String[] array. Basic primitives.
This is impossible to avoid as all custom classes are simply wrappers for primitives, no matter what you implement.
Table of Contents
Why use collections?
Collections make you more efficient, need less code and are convenient for the programmer. The reason, these data structures are meant to be used with custom classes. If you make say the Coordinate class,
public class Coordinate {
private int x;
private int y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public static void main(String[] args) {
Coordinate point = new Coordinate(3, 4);
Coordinate point2 = new Coordinate(6, 7);
Coordinate point3 = new Coordinate(4, 5);
Map<String, Coordinate> map = new HashMap<>();
map.put("Alice", point);
map.put("Bob", point2);
map.put("Charlie", point3);
}
}
You can add it to any collection data structure. This is where all the power of collections comes from. You can use either primitives or custom classes, organizing and storing in an optimal way.
On the lowest level though, there is some bloat to these data structures. They cost a little more memory and processing power than the primitive versions of the data structures.
Meaning a List in C# is more bloated both in memory and processing than an array. What we sacrifice we more than make up for in the ease of making applications for the programmer.
Types of Collections
These would be considered part of programming fundamentals. They are standard in each language. It is always good practice to use these collections for your applications.
Lists
A List is a mutable string of objects. Remember we can place any class here, including primitives. Let’s look at it in C#:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Create a new List of integers
List<int> numbers = new List<int>();
// Add elements to the List
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
numbers.Add(4);
numbers.Add(5);
// Access elements by index
Console.WriteLine("Second element: " + numbers[1]);
}
}
We have basically an array [1, 2, 3, 4, 5]. There really is no difference between an array and a list.
The Collection, list, just includes functions which wrap around the array. Take a look:
class Program
{
static void Main()
{
List<int> list = new List<int>();
int index = 3;
int item = 5;
list.RemoveAt(index);
list.Insert(index, item);
list.Clear();
list.Contains(item);
list.IndexOf(item);
list.Count;
list.Sort();
list.Reverse();
//etc.
}
}
Primitive arrays do not come with those functions. Yes Lists are simply arrays, but they are arrays with functions.
Sets
Sets are a collection of elements which are unique. They are a List, except there are no duplicate elements. In Sets we limit the functions to only those which are hash-able. This allows the data structure to map each element in the data structure.
Lets look at an example:
import java.util.HashSet;
import java.util.Set;
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Orange");
set.remove("Banana");
boolean containsOrange = set.contains("Orange");
}
The power behind sets is where that add and remove banana is actually mapped. There are fewer functions for Sets than Lists, but this is done intentionally. It is a dictionary in the background. If you know your elements are going to be unique, then that is the moment to use a Set.
This means less processing and less memory usage.
Maps
These are dictionaries. You get key-value pairs for the fastest processing possible. You call a value by its key, AKA its name, so you do not have to iterate over the entire data structure.
Lets look at an example:
#include <iostream>
#include <map>
int main() {
// Create a map of string keys and int values
std::map<std::string, int> ageMap;
// Insert key-value pairs into the map
ageMap["Alice"] = 30;
ageMap["Bob"] = 25;
ageMap["Charlie"] = 35;
// Access a value by its key
std::cout << "Age of Bob: " << ageMap["Bob"] << std::endl;
}
We call “Bob” as the number 25. So whenever we ask for “Bob” in the age map we get 25 the quickest possible. The main feature of maps is how huge we can make the entire data structure and still only have O(1) or just 1 super fast function call for its value, like 25.
Queue
Queues are a First-In-First-Out data structure. They are meant for any type of waiting line in processing. They are used everywhere in programming. However nowadays people just use Lists and only use the queue-like functions.
Here is an example of a Queue:
import java.util.LinkedList;
import java.util.Queue;
public static void main(String[] args) {
// Create a new LinkedList as a Queue
Queue<String> queue = new LinkedList<>();
// Add elements to the queue
queue.offer("Apple");
queue.offer("Banana");
queue.offer("Orange");
// Peek at the head of the queue without removing it
System.out.println("Head of the queue: " + queue.peek());
// Remove and return the head of the queue
System.out.println("Removed from the queue: " + queue.poll());
}
Stack
Stacks are in essence the same as queues except it is Last-In-First-Out. Instead of getting the beginning of the list, we get the end.
Like said before most people just use the List data structure but use the stack-like functions.
Here is how it looks:
#include <iostream>
#include <stack>
int main() {
// Create a stack of integers
std::stack<int> stack;
// Push elements onto the stack
stack.push(1);
stack.push(2);
stack.push(3);
// Access the top element of the stack
std::cout << "Top of the stack: " << stack.top() << std::endl;
}
Choosing the right type
Choosing the right type of Collection is a chicken and egg problem. You need to have used the data structures in the past to know what Collection to use right now.
I recommend experimenting with the different collections. Take the descriptions I gave you above and use them. It is good to use them wrongly and realize it was wrong later. Fix the error and learn a way not to use that data structure.
The best learning methods are trial and error, reading other peoples code, having your code reviewed and solving LeetCode problems.
As you learn all the ways collections are used you develop an instinct for data structures. You eventually will be able to just look at a problem and quickly know which data structure to use. Other people’s code can help clarify that instinct and code reviews will be teaching moments for your own code.
Lastly, You will be surprised how dependent LeetCode is on Collections. The problem descriptions always ask for a specific Collection to be used. LeetCode problems are a great way to figure out when and when not to use data structures.
Anywho, I hope you learned something…
CTA: Check out my book on learning code
Happy coding!
Resources
Programming fundamentals: https://jessenerio.com/why-coding-fundamentals-are-important-questions-you-should-be-asking/