The rule that makes a set different from a list is simple: a set holds each value only once. Adding a value that is already there changes nothing.
Set<String> tags = new Set<String>();
tags.add('urgent');
tags.add('urgent');
System.debug(tags.size()); // 1
The second add is not an error and it does not throw anything. The set simply already had that value, so nothing changed. This is why you can add values in a loop without ever checking first.
The add() method hands back a Boolean that tells you if the set actually grew:
Set<Integer> codes = new Set<Integer>{ 100 };
Boolean addedNew = codes.add(200); // true, the set grew
Boolean addedOld = codes.add(100); // false, 100 was already there
The most common use of this rule is removing duplicates from a list. Build a set from the list and the repeats disappear on their own:
List<String> visits = new List<String>{ 'apex', 'soql', 'apex' };
Set<String> unique = new Set<String>(visits);
System.debug(unique.size()); // 2
Because the duplicates are gone, size() is the count of distinct values.
Do not expect the set to remember the order the values arrived in, and do not expect size() to match the size of the list you started from. Losing the repeats is the whole point.
Counting distinct values and removing duplicates are everyday tasks: unique account ids to query, unique email addresses to notify, unique error codes to report. A set gives you all of that for free.