Set
HashSet
Set<String> hset = new HashSet<String>(); // String: immutable elements
hset.add("platform"); // mutable collection
hset.add("rpg");
hset.add("strategy");
true
System.out.println(hset); // unordered collection
[rpg, strategy, platform]
for (String element : hset){ // unordered collection -> unpredictable order!
System.out.println(element);
}
rpg strategy platform
hset.get(0) // no order -> no way to get first element!
| hset.get(0)
cannot find symbol
symbol: method get(int)
hset.add("beat'em up");
true
hset.add("beat'em up"); // distinct elements -> duplicates are silently discarded
false
hset.remove("strategy");
true
hset.remove("strategy"); // removals fail silently
false
System.out.println(hset); // no duplicates
[beat'em up, rpg, platform]
-> does not depend on set size
hset.contains("rpg") // super fast
true
public class PoorMonster {
// stuff that matters
public String name = "";
public int lifepoints = 0;
// Java rituals
@Override
public String toString() {
return "PoorMonster{" + "name=" + name + ", lifepoints=" + lifepoints + '}';
}
// NO hashCode NOR equals !
}
PoorMonster poor1 = new PoorMonster();
poor1.name = "Mr Poor";
poor1.lifepoints = 1;
PoorMonster poor2 = new PoorMonster();
poor2.name = "Mr Poor";
poor2.lifepoints = 1;
1
Set<PoorMonster> poorset = new HashSet<PoorMonster>();
poorset.add(poor1);
poorset.add(poor1);
System.out.println(poorset);
[PoorMonster{name=Mr Poor, lifepoints=1}]
poorset.add(poor2);
true
System.out.println(poorset); // We've got duplicates !
[PoorMonster{name=Mr Poor, lifepoints=1}, PoorMonster{name=Mr Poor, lifepoints=1}]
Using Monster
defined as in classes slides
import java.util.Objects;
public class Monster {
// stuff that matters
public String name = "";
public int lifepoints = 0;
// Java rituals ......................
@Override
public String toString() {
return "Monster{" + "name=" + name + ", lifepoints=" + lifepoints + '}';
}
@Override
public int hashCode() {
int hash = 3;
hash = 89 * hash + Objects.hashCode(this.name);
hash = 89 * hash + this.lifepoints;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Monster other = (Monster) obj;
if (this.lifepoints != other.lifepoints) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return true;
}
}
Monster cerberus1 = new Monster();
cerberus1.name = "Cerberus";
cerberus1.lifepoints = 7;
Monster cerberus2 = new Monster();
cerberus2.name = "Cerberus";
cerberus2.lifepoints = 7;
Monster zombo = new Monster();
zombo.name = "Zombo";
zombo.lifepoints = 4;
4
Set<Monster> mset = new HashSet<Monster>();
mset.add(cerberus1);
true
System.out.println(mset)
[Monster{name=Cerberus, lifepoints=7}]
mset.add(cerberus2);
false
System.out.println(mset)
[Monster{name=Cerberus, lifepoints=7}]
mset.add(zombo);
System.out.println(mset)
[Monster{name=Cerberus, lifepoints=7}, Monster{name=Zombo, lifepoints=4}]
mset.contains(cerberus1);
true
mset.contains(cerberus2);
true
mset.remove(cerberus1);
true
mset.remove(cerberus2); // silent fail
false
Monster mutantGoblin1 = new Monster();
mutantGoblin1.name = "Mutant Goblin";
mutantGoblin1.lifepoints = 3; // NOTE 3
3
Monster mutantGoblin2 = new Monster();
mutantGoblin2.name = "Mutant Goblin";
mutantGoblin2.lifepoints = 5; // NOTE 5
5
System.out.println(mutantGoblin1);
System.out.println(mutantGoblin2);
Monster{name=Mutant Goblin, lifepoints=3} Monster{name=Mutant Goblin, lifepoints=5}
Set<Monster> brokenSet = new HashSet<Monster>();
brokenSet.add(mutantGoblin1);
brokenSet.add(mutantGoblin2);
System.out.println(brokenSet);
[Monster{name=Mutant Goblin, lifepoints=3}, Monster{name=Mutant Goblin, lifepoints=5}]
So far so good but ....
mutantGoblin1.lifepoints = 5; // Watch out !
5
System.out.println(brokenSet);
[Monster{name=Mutant Goblin, lifepoints=5}, Monster{name=Mutant Goblin, lifepoints=5}]
Very bad! We broke the set!
If you really need to mutate an element:
Readings: MIT Mutability & Immutability
Keys:
-> keys behave like items of a set
Values:
Given a key, we can find the corresponding value very fast
Monster cerberus1 = new Monster();
cerberus1.name = "Cerberus";
cerberus1.lifepoints = 7;
Monster zombo = new Monster();
zombo.name = "Zombo";
zombo.lifepoints = 4;
Monster mutantBlob = new Monster();
mutantBlob.name = "Mutant Blob";
mutantBlob.lifepoints = 6; // NOTE 2
6
HashMap<String, Monster> castle = new HashMap();
castle.put("lab", cerberus1);
castle.put("pit", zombo);
castle.put("tower", mutantBlob);
System.out.println(castle); // iteration is by keys, unpredictable order!
{pit=Monster{name=Zombo, lifepoints=4}, lab=Monster{name=Cerberus, lifepoints=7}, tower=Monster{name=Mutant Blob, lifepoints=6}}
for (String key : castle.keySet()){ // unpredictable order!
System.out.println(key + ": " + castle.get(key));
}
pit: Monster{name=Zombo, lifepoints=4} lab: Monster{name=Cerberus, lifepoints=7} tower: Monster{name=Mutant Blob, lifepoints=6}
-> does not depend on map size!
System.out.println( castle.get("pit") ) // very fast!
Monster{name=Zombo, lifepoints=4}
castle.put("pit", mutantBlob); // silent key replacement
Monster{name=Zombo, lifepoints=4}
System.out.println(castle); // values can be duplicated
{pit=Monster{name=Mutant Blob, lifepoints=6}, lab=Monster{name=Cerberus, lifepoints=7}, tower=Monster{name=Mutant Blob, lifepoints=6}}
castle.get("pit").lifepoints = 900; // mutating values is fine
900
System.out.println(castle) // even if we have more references to same object
{pit=Monster{name=Mutant Blob, lifepoints=900}, lab=Monster{name=Cerberus, lifepoints=7}, tower=Monster{name=Mutant Blob, lifepoints=900}}
System.out.println( castle.get("playground") ) // silent fail
null
We can!
HashMap<Monster, String> dungeon = new HashMap();
dungeon.put(cerberus1, "hall");
dungeon.put(zombo, "lab");
System.out.println(dungeon);
{Monster{name=Cerberus, lifepoints=7}=hall, Monster{name=Zombo, lifepoints=4}=lab}
dungeon.put(cerberus1, "corridor"); // we can reassign key/value
hall
System.out.println(dungeon);
{Monster{name=Cerberus, lifepoints=7}=corridor, Monster{name=Zombo, lifepoints=4}=lab}
dungeon.put(cerberus2, "pit"); // if objects have equals() method, key will be replaced
corridor
System.out.println(dungeon);
{Monster{name=Cerberus, lifepoints=7}=pit, Monster{name=Zombo, lifepoints=4}=lab}
Remember poor monsters don't have .equals(Object)
nor hashCode()
HashMap<PoorMonster, String> village = new HashMap(); // we inverted key/value types
PoorMonster beggarGoblin1 = new PoorMonster();
beggarGoblin1.name = "Beggar Goblin";
beggarGoblin1.lifepoints = 2;
PoorMonster beggarGoblin2 = new PoorMonster();
beggarGoblin2.name = "Beggar Goblin";
beggarGoblin2.lifepoints = 2;
2
village.put(beggarGoblin1, "street"); // works
System.out.println(village);
{PoorMonster{name=Beggar Goblin, lifepoints=2}=street}
village.put(beggarGoblin1, "market" ); // works, replaces key/value
System.out.println(village);
{PoorMonster{name=Beggar Goblin, lifepoints=2}=market}
village.put(beggarGoblin2, "jail"); // no .equals(Object) -> different keys!
System.out.println(village);
{PoorMonster{name=Beggar Goblin, lifepoints=2}=market, PoorMonster{name=Beggar Goblin, lifepoints=2}=jail}
Monster zombo = new Monster();
zombo.name = "Zombo";
zombo.lifepoints = 4;
Monster mutantGoblin1 = new Monster();
mutantGoblin1.name = "Mutant Goblin";
mutantGoblin1.lifepoints = 3; // NOTE 3
Monster mutantGoblin2 = new Monster();
mutantGoblin2.name = "Mutant Goblin";
mutantGoblin2.lifepoints = 5; // NOTE 5
5
HashMap<Monster, String> brokenMap = new HashMap();
brokenMap.put(mutantGoblin1, "torture room");
System.out.println(brokenMap);
{Monster{name=Mutant Goblin, lifepoints=3}=torture room}
brokenMap.put(mutantGoblin2, "jail" );
System.out.println(brokenMap);
{Monster{name=Mutant Goblin, lifepoints=3}=torture room, Monster{name=Mutant Goblin, lifepoints=5}=jail}
brokenMap.get(mutantGoblin1)
torture room
mutantGoblin1.lifepoints = 5 // WARNING: we're mutating attributes of a key!!!
5
System.out.println(brokenMap);
{Monster{name=Mutant Goblin, lifepoints=5}=torture room, Monster{name=Mutant Goblin, lifepoints=5}=jail}
Monster
class has proper .equals(Object)
and hashCode()
methods...