List,
Set, Map are called collections in Apex.
List
: 
A list is an ordered collection.
1)
so use list when you want to identify list element based on Index
Number.
2)
list can contain Duplicates.
EX:
List<Account> accList = new List<Account>();
Set
:
A set is an unordered collection.
1)
Do not contain any duplicate elements. So, use set if you want to
make sure that your collection should not contain Duplicates.
EX:
Set<Account> accSet = new Set<Account>();
Set<String>
setString = new Set<String>();
//
Add two strings to it
setString
.add('item1');
setString
.add('item2');
Map
: 
A map is a collection of key-value pairs.
Each
unique key maps to a single value. Keys can be any primitive data
type, while values can be a primitive, sObject, collection type or an
Apex object.
EX:
Map<Id, Account> accMap = new Map<Id, Account>();
Map<Integer,
String> mapOfString = new Map<Integer, String>();
mapOfString.put(1,
'string1');               
mapOfString.put(2,
'string2');              
System.assert(mapOfString.containsKey(1));
String
value = mapOfString.get(2); 
System.assertEquals('string2',
value);
Set<Integer>
s = mapOfString.keySet();
Map<String,
String> myMap = new Map<String, String>{'a' => 'b', 'c'
=> 'd'};
Example
1:- 
Trigger to Populate the Account Field(Accunt Type) on the Contact record(In Type__C Custom Field)   (only insert scenario).
If
we use List and not Map
Apex
Trigger:
trigger ContactTriggerWithList on Contact (before insert){
 Set<Id> SetAccountId = new Set<Id>(); // Use set to collect unique account ID
 for(Contact con: Trigger.new){
  if(con.AccountId != null){
   SetAccountId.add(con.AccountId); // add Account in Set
  }
 }
 if( SetAccountId.size() >0 ){
  List<Account> listAccount = [ Select Id, Name, Type from Account where Id IN :SetAccountId ]; // Query Related Account
  for(Contact con: Trigger.new) {
   if(con.AccountId != null){
    for(Account acc: listAccount){
     if(con.AccountId == acc.Id){
      con.Type__c = acc.Type;
     }
    }
   }
  }
 }   
}
NOTE:-
In this we are using List of Accounts, Where we have to loop through
the matching Account every time to populate the Type__c in the second
for loop. In this case need to use nested loop.
Instead of  Nested Loop We can use the Map.
Same
Trigger using the Map:
trigger ContactTriggerWithMap on Contact (before insert){
    Set<Id> SetAccountId = new Set<Id>();
    for(Contact cont: Trigger.new) {
        if(cont.AccountId != null) {
            SetAccountId.add(cont.AccountId);
        }
    }
    if(SetAccountId.size() > 0 ) {
        Map<Id, Account> mapAccount = new Map<Id, Account>([Select Id, Name, Type from Account where Id IN :SetAccountId ]);
        for(Contact cont: Trigger.new){
            if(cont.AccountId != null && mapAccount.containsKey(cont.AccountId) ) {
                cont.Type__c = mapAccount.get(cont.AccountId).Type;
            }
        }
    }
}
NOTE:-
Here we are using Map of Account so we can directly use the Map to
get the Account record. No need of 2nd for loop here.
