Animals.java
The following program have several problems:
1. hasAnimal can throw ConcurrentModificationException when multiple threads call load and hasAnimal simultaneously.
2. the helper thread only start to work after the main thread finish loading the ark.
3. Set<AnimalPair> loadedAnimals can have duplicates.
import java.util.*;
public class Animals {
Ark ark;
Species species;
Gender gender;
public int loadTheArk(Collection<Animal> candidates) {
SortedSet<Animal> animals;
int numPairs = 0;
Animal candidate = null;
// animals confined to method, don't let them escape!
animals = new TreeSet<Animal>(new SpeciesGenderComparator());
animals.addAll(candidates);
for (Animal a : animals) {
if (candidate == null || !candidate.isPotentialMate(a))
candidate = a;
else {
ark.load(new AnimalPair(candidate, a));
++numPairs;
candidate = null;
}
}
return numPairs;
}
public static void main(String...args) {
Animals animals = new Animals();
animals.ark = animals.new Ark();
Collection<Animal> candidates = new TreeSet<Animal>(animals.new SpeciesGenderComparator());
for(Species s: Species.values()) {
for(Gender g: Gender.values()) {
if(s.toString().charAt(0) >= 'H') {
candidates.add(animals.new Animal(s, g));
}
}
}
System.out.println(animals.loadTheArk(candidates) + " pairs of animals loaded");
System.out.println(Species.UNICORN + " is on the ark? " + animals.ark.hasAminal(Species.UNICORN));
System.out.println(Species.AARDVARK + " is on the ark? " + animals.ark.hasAminal(Species.AARDVARK));
//can start helper earlier
Thread helper = new Thread(new Runnable(){
@Override
public void run() {
Collection<Animal> candidates2 = new TreeSet<Animal>(animals.new SpeciesGenderComparator());
for(Species s: Species.values()) {
for(Gender g: Gender.values()) {
if(s.toString().charAt(0) <= 'H') {
candidates2.add(animals.new Animal(s, g));
}
}
}
System.out.println(animals.loadTheArk(candidates2) + " pairs of animals loaded");
System.out.println(Species.UNICORN + " is on the ark? " + animals.ark.hasAminal(Species.UNICORN));
System.out.println(Species.AARDVARK + " is on the ark? " + animals.ark.hasAminal(Species.AARDVARK));
}});
helper.start();
}
class Animal {
Species species;
Gender gender;
public Animal(Species species, Gender gender) {
this.species = species;
this.gender = gender;
}
public boolean isPotentialMate(Animal other) {
return species == other.species && gender != other.gender;
}
}
enum Species {
AARDVARK, BENGAL_TIGER, CARIBOU, DINGO, ELEPHANT, FROG, GNU, HYENA,
IGUANA, JAGUAR, KIWI, LEOPARD, MASTADON, NEWT, OCTOPUS,
PIRANHA, QUETZAL, RHINOCEROS, SALAMANDER, THREE_TOED_SLOTH,
UNICORN, VIPER, WEREWOLF, XANTHUS_HUMMINBIRD, YAK, ZEBRA
}
enum Gender {
MALE, FEMALE
}
class AnimalPair {
//logical equality?
private final Animal one, two;
public AnimalPair(Animal one, Animal two) {
this.one = one;
this.two = two;
}
}
class SpeciesGenderComparator implements Comparator<Animal> {
public int compare(Animal one, Animal two) {
int speciesCompare = one.species.compareTo(two.species);
return (speciesCompare != 0)
? speciesCompare
: one.gender.compareTo(two.gender);
}
}
class Ark {
// synchronized wrapper classes support client-side locking, using the wrapper collection (loadedAnimals)
private final Set<AnimalPair> loadedAnimals = Collections.synchronizedSet(new HashSet<AnimalPair>());
public void load(AnimalPair pair) {
loadedAnimals.add(pair);
}
public boolean hasAminal(Species s) {
for(AnimalPair ap: loadedAnimals) {
//load can be changing the loadedAnimals while you are iterating
if(ap.one.species.compareTo(s) == 0) {
return true;
}
}
return false;
}
}
}