Java 學習記錄66 — Maps 2/3

張小雄
5 min readJun 1, 2021

Location.java

import java.util.HashMap;
import java.util.Map;
public class Location {
private final int locationId;
private final String description;
private final Map<String, Integer> exits;
public Location(int locationId, String description) {
this.locationId = locationId;
this.description = description;
this.exits = new HashMap<String, Integer>();
}
public void addExit(String direction, int location) {
exits.put(direction, location);
}
public int getLocationId() {
return locationId;
}
public String getDescription() {
return description;
}
public Map<String, Integer> getExits() {
return new HashMap<String, Integer>(exits);
}
}

MainLocation.java

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class MainLocation { private static Map<Integer, Location> locations = new HashMap<Integer, Location>(); public static void main(String[] args) { Scanner scanner = new Scanner(System.in); locations.put(0, new Location(0, "You are sitting in front of a computer learning Java."));
locations.put(1, new Location(1, "You are standing at the end of road before a small brick building."));
locations.put(2, new Location(2, "You are at the top of a hill."));
locations.put(3, new Location(3, "You are inside a building, a well house for a small spring."));
locations.put(4, new Location(4, "You are in a valley beside a stream."));
locations.put(5, new Location(5, "You are in the forest."));
int loc = 1;
while (true) {
System.out.println(locations.get(loc).getDescription());
if (loc == 0) {
break;
}
loc = scanner.nextInt();
if (!locations.containsKey((loc))) {
System.out.println("You cannot go in that direction");
}
}
}
}

輸出結果:

You are standing at the end of road before a small brick building.

2

You are at the top of a hill.

3

You are inside a building, a well house for a small spring.

5

You are in the forest.

4

You are in a valley beside a stream.

key 跟 value 可以是任意 object

Tim 補充說:不像是 Python 規定 key 一定要是 immutable object,Java 無此規定

任何不需要 public,都設為 private

final 則是只要 reference 過,就不能再 reference

例如:

this.exits = new HashMap<String, Integer>(exits);

this.exits = new HashMap<String, Integer>(exits);

如果 filed 有加 final,如 private final Map<String, Integer> exits;

這邊就會直接報錯,而沒有加就沒事

此處案例是要實現一個簡單版本的古老的遊戲 — colossal cave adventure

為什麼要用 return new HashMap<String, Integer>(exits)

Tim:

I’m putting exits in the constructor

so the reason we’re doing that is that instead of just returning the exits map and creating a new HashMap and passing out exits in the constructor on line 32.

now we’ve seen that use of the constructor before with lists it does the same thing here, so in other words a new map created that has all the mapings of the exits map.

now the reasons that this would be useful technique doing that here is that nothing outside of that class can change exits.

so the getter returns a copy of the exits so if the calling program wants to add or remove mappings from it

and the exits mapping field won’t be affected that’s really important there

so although we won’t be using the location class as a key in the map,

we use that later to show some ways to make your classes immutable

and returning immutable object to getter in this way is one of the techniques here to make sure that works

大意應該是避免 exits 資料被修改的機會

補充:

為啥改成 final 後還能新增刪除資料等

Location.java 改

import java.util.HashMap;
import java.util.Map;
public class Location {
private final int locationId;
private final String description;
private final Map<String, Integer> exits;
public Location(int locationId, String description) {
this.locationId = locationId;
this.description = description;
this.exits = new HashMap<String, Integer>();
}
public void addExit(String direction, int location) {
exits.put(direction, location);
}
public int getLocationId() {
return locationId;
}
public String getDescription() {
return description;
}
public Map<String, Integer> getExits() {
return new HashMap<String, Integer>(exits);
}
}

MainLocation.java 改

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class MainLocation { private static Map<Integer, Location> locations = new HashMap<Integer, Location>(); public static void main(String[] args) { Scanner scanner = new Scanner(System.in); locations.put(0, new Location(0, "You are sitting in front of a computer learning Java."));
locations.put(1, new Location(1, "You are standing at the end of road before a small brick building."));
locations.put(2, new Location(2, "You are at the top of a hill."));
locations.put(3, new Location(3, "You are inside a building, a well house for a small spring."));
locations.put(4, new Location(4, "You are in a valley beside a stream."));
locations.put(5, new Location(5, "You are in the forest."));
locations.get(1).addExit("W", 2);
locations.get(1).addExit("E", 3);
locations.get(1).addExit("S", 4);
locations.get(1).addExit("N", 5);
locations.get(1).addExit("Q", 0);
locations.get(2).addExit("N", 5);
locations.get(2).addExit("Q", 0);
locations.get(3).addExit("W", 1);
locations.get(3).addExit("Q", 0);
locations.get(4).addExit("N", 1);
locations.get(4).addExit("W", 2);
locations.get(4).addExit("Q", 0);
locations.get(5).addExit("W", 2);
locations.get(5).addExit("S", 1);
locations.get(5).addExit("Q", 0);
int loc = 1;
while (true) {
System.out.println(locations.get(loc).getDescription());
if (loc == 0) {
break;
}
Map<String, Integer> exits = locations.get(loc).getExits();
System.out.println("Available exits are ");
for (String exit : exits.keySet()) {
System.out.println(exit + ", ");
}
System.out.println();
String direction = scanner.nextLine().toUpperCase();
if (exits.containsKey(direction)) {
loc = exits.get(direction);
} else {
System.out.println("You cannot go in that direction");
}
} }
}

輸出結果:

You are standing at the end of road before a small brick building.

Available exits are

Q,

S,

E,

N,

W,

N

You are in the forest.

Available exits are

W,

W

You are at the top of a hill.

Available exits are

Q,

N,

E

You cannot go in that direction

You are at the top of a hill.

Available exits are

Q,

N,

S

You cannot go in that direction

You are at the top of a hill.

Available exits are

Q,

N,

S,

W,

W

You are at the top of a hill.

Available exits are

Q,

N,

E

You cannot go in that direction

You are at the top of a hill.

Available exits are

Q,

N,

S

You cannot go in that direction

You are at the top of a hill.

Available exits are

Q,

N,

展示了 map 如何用 for 循環

在這邊才會了解前面,為什麼要把第二個放上 location 當參數

locations.put(0, new Location(0, "You are sitting in front of a computer learning Java."));

就是要讓對應的 map 多出可移動的方向

現在可以根據提示移動地圖

選完又可以用對應的 value 當 key 回到,外層對應的 map

例如,選完 key:W,又可以用其對應的 value:2

locations.get(1).addExit("W", 2);

回到 locations.put(2, new Location(2, "You are at the top of a hill."))

如此循環往復,若看不懂 code 可以開 debug 一步一步看

這邊設計的還滿巧妙的,我一開始也是根本看不懂

Tim 講解前面 addExit 的部份,為啥要那樣寫:

On line 47 we’re using our get access method to retrieve the map of valid exits from the current location

and as get exits to return a copy if we did make any changes to that map it wouldn’t affect the map field in the location instance

Location.java 改

import java.util.HashMap;
import java.util.Map;
public class Location {
private final int locationId;
private final String description;
private final Map<String, Integer> exits;
public Location(int locationId, String description) {
this.locationId = locationId;
this.description = description;
this.exits = new HashMap<String, Integer>();
this.exits.put("Q", 0);
}
public void addExit(String direction, int location) {
exits.put(direction, location);
}
public int getLocationId() {
return locationId;
}
public String getDescription() {
return description;
}
public Map<String, Integer> getExits() {
return new HashMap<String, Integer>(exits);
}
}

MainLocation.java 改

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class MainLocation { private static Map<Integer, Location> locations = new HashMap<Integer, Location>(); public static void main(String[] args) { Scanner scanner = new Scanner(System.in); locations.put(0, new Location(0, "You are sitting in front of a computer learning Java."));
locations.put(1, new Location(1, "You are standing at the end of road before a small brick building."));
locations.put(2, new Location(2, "You are at the top of a hill."));
locations.put(3, new Location(3, "You are inside a building, a well house for a small spring."));
locations.put(4, new Location(4, "You are in a valley beside a stream."));
locations.put(5, new Location(5, "You are in the forest."));
locations.get(1).addExit("W", 2);
locations.get(1).addExit("E", 3);
locations.get(1).addExit("S", 4);
locations.get(1).addExit("N", 5);
locations.get(2).addExit("N", 5); locations.get(3).addExit("W", 1); locations.get(4).addExit("N", 1);
locations.get(4).addExit("W", 2);
locations.get(5).addExit("W", 2);
locations.get(5).addExit("S", 1);
int loc = 1;
while (true) {
System.out.println(locations.get(loc).getDescription());
if (loc == 0) {
break;
}
Map<String, Integer> exits = locations.get(loc).getExits();
System.out.println("Available exits are ");
for (String exit : exits.keySet()) {
System.out.println(exit + ", ");
}
System.out.println();
String direction = scanner.nextLine().toUpperCase();
if (exits.containsKey(direction)) {
loc = exits.get(direction);
} else {
System.out.println("You cannot go in that direction");
}
} }
}

輸出結果:

You are standing at the end of road before a small brick building.

Available exits are

Q,

S,

E,

N,

W,

q

You are sitting in front of a computer learning Java.

每個地圖用來離開的 Q,放到了 Location 的 instructer 裡面,就不需要重複寫代碼了

--

--