Java 學習記錄73 — Sets & HashSet — 5/5
Tags: Coding, Programming, Java, Learning, Taiwan

小練習：

Modify the previous HeavenlyBody example so that the HeavenlyBody

class also has a “bodyType” field. This field will store the

type of HeavenlyBody (such as STAR, PLANET, MOON, etc).

You can include as many types as you want, but must support at

least PLANET and MOON.

For each of the types that you support, subclass the HeavenlyBody class

so that your program can create objects of the appropriate type.

Although astronomers may shudder at this, our solar systems will

allow two bodies to have the same name as long as they are not the

same type of body: so you could have a star called “BetaMinor” and

an asteroid also called “BetaMinor”, for example.

Hint: This is much easier to implement for the Set than it is for the Map,

because the Map will need a key that uses both fields.

There is a restriction that the only satellites that planets can have must

be moons. Even if you don’t implement a STAR type, though, your program

should not prevent one being added in the future (and a STAR’s satellites

can be almost every kind of HeavenlyBody).

Test cases:

1. The planets and moons that we added in the previous video should appear in

the solarSystem collection and in the sets of moons for the appropriate planets.

2. a.equals(b) must return the same result as b.equals(a) — equals is symmetric.

3. Attempting to add a duplicate to a Set must result in no change to the set (so

the original value is not replaced by the new one).

4. Attempting to add a duplicate to a Map results in the original being replaced

by the new object.

5. Two bodies with the same name but different designations can be added to the same set.

6. Two bodies with the same name but different designations can be added to the same map,

and can be retrieved from the map.

參考答案：

HeavenlyBody.java

package SetChallenge; import java.util.HashSet;

import java.util.Set; public abstract class HeavenlyBody {

private final Key key;

private final double orbitalPeriod;

private final Set<HeavenlyBody> satellites; public static Key makeKey(String name, BodyTypes bodyType) {

return new Key(name, bodyType);

} public HeavenlyBody(String name, double orbitalPeriod, BodyTypes bodyType) {

this.orbitalPeriod = orbitalPeriod;

this.satellites = new HashSet<>();

this.key = new Key(name, bodyType); } public double getOrbitalPeriod() {

return orbitalPeriod;

} public Key getKey() {

return key;

} public boolean addSatellite(HeavenlyBody satellite) {

return this.satellites.add(satellite);

} public Set<HeavenlyBody> getSatellites() {

return new HashSet<>(this.satellites);

} @Override

public final boolean equals(Object obj) {

if (this == obj) {

return true;

}

if (obj instanceof HeavenlyBody) {

Key objKey = ((HeavenlyBody) obj).getKey();

return this.key.equals(objKey);

}

return false;

} @Override

public final int hashCode() {

return this.key.hashCode();

} @Override

public String toString() {

return getKey().getName() + ": " + getKey().getBodyType() + ", " + getOrbitalPeriod();

}

enum BodyTypes {

PLANET, DWARF_PLANET, MOON

} public static final class Key {

private String name;

private BodyTypes bodyType; private Key(String name, BodyTypes bodyType) {

this.name = name;

this.bodyType = bodyType;

} public String getName() {

return name;

} public BodyTypes getBodyType() {

return bodyType;

} @Override

public boolean equals(Object obj) {

if (this == obj) {

return true;

}

if ((obj == null) || (obj.getClass() != this.getClass())) {

System.out.println("obj is null or not equal!");

return false;

} String objName = ((HeavenlyBody.Key) obj).getName();

BodyTypes objBodyType = ((HeavenlyBody.Key) obj).getBodyType();

return this.name.equals(objName) && this.bodyType.equals(objBodyType);

} @Override

public int hashCode() {

return this.name.hashCode() + this.bodyType.hashCode() + 37;

} @Override

public String toString() {

return name + ": " + bodyType;

}

}

}

class Planet extends HeavenlyBody {

public Planet(String name, double orbitalPeriod) {

super(name, orbitalPeriod, BodyTypes.PLANET);

} @Override

public boolean addSatellite(HeavenlyBody moon) {

if (moon.getKey().getBodyType() == BodyTypes.MOON) {

return super.addSatellite(moon);

}

return false; }

}

class DwarfPlanet extends HeavenlyBody {

public DwarfPlanet(String name, double orbitalPeriod) {

super(name, orbitalPeriod, BodyTypes.DWARF_PLANET);

}

}

class Moon extends HeavenlyBody {

public Moon(String name, double orbitalPeriod) {

super(name, orbitalPeriod, BodyTypes.MOON);

}

}

Dang Nam

The reason is that a map can’t have duplicate keys. The 6th requirement of this challenge is to allow bodies with the same name but different types to be added (types are Planet, Moon, DwarfPlanet, and such). As you may have noticed, our Map only contains String keys, so an attempt to store bodies with the same name and different types would fail since you can’t have duplicate keys. What I’m trying to say is, let’s say you have 2 keys: Pluto (type Planet) and Pluto (type DwarfPlanet). For example, if you wrote [map.put(“Pluto”, new Planet(“Pluto”, 90.8);] then you won’t be able to do [map.put(“Pluto”, new DwarfPlanet(“Pluto”, 64.6));] because they both have the same String key “Pluto”. So you need to find a way to overcome this and it is storing objects that contain both name and type as keys. A Key object contains name and bodyType so we will be able to add 2 keys that contain the same name (Pluto) but of different type (one is Planet and the other is DwarfPlanet). The reason for this is the equals() method inside Key class, the method compares the 2 object’s name then it compares the 2 object’s bodyType, if any of these are different it will return false. When you add a key to a map, it will check to see if there’s already a key in a map that will return true when compare to the key you want to add, but since you’re adding 2 elements with the same name but different bodyType the comparison will return false, which allows you to add both of them to the map.

My apology for the wordy and lengthy explanation, I hope it helps!

Goran

public static final class Key {

private String name;

private BodyTypes bodyType;

private Key(String name, BodyTypes bodyType) {

this.name = name;

this.bodyType = bodyType;

}

public String getName() {

return name;

}

public BodyTypes getBodyType() {

return bodyType;

}

@Override

public int hashCode() {

return this.name.hashCode() + 57 + this.bodyType.hashCode();

}

@Override

public boolean equals(Object obj) {

Key key = (Key) obj;

if(this.name.equals(key.getName())) {

return(this.bodyType == key.getBodyType());

}

return false;

}

@Override

public String toString() {

return this.name + ": " + this.bodyType;

}

}