Java stream - find most frequent element based on a specific field - java

I have a list of Person objects, I would like to find the most frequent name in the list, and the frequency, only using java streams. (When there is a tie, return any result)
Currently, my solution uses groupingBy and counting, then again finding the max element in the resulting map.
The current solution makes 2 passes on the input (list/map).
Is it possible to make this a bit more efficient and readable?
Person p1 = Person.builder().id("p1").name("Alice").age(1).build();
Person p2 = Person.builder().id("p2").name("Bob").age(2).build();
Person p3 = Person.builder().id("p3").name("Charlie").age(3).build();
Person p4 = Person.builder().id("p4").name("Alice").age(4).build();
List<Person> people = ImmutableList.of(p1, p2, p3, p4);
Map.Entry<String, Long> mostCommonName = people
.stream()
.collect(collectingAndThen(groupingBy(Person::getName, counting()),
map -> map.entrySet().stream().max(Map.Entry.comparingByValue()).orElse(null)
));
System.out.println(mostCommonName); // Alice=2

If you are insisting on only using streams then your best bet is likely to have a custom collector that includes the info required to aggregate in a single pass:
class MaxNameFinder implements Collector<Person, ?, String> {
public class Accumulator {
private final Map<String,Integer> nameFrequency = new HashMap<>();
private int modeFrequency = 0;
private String modeName = null;
public String getModeName() {
return modeName;
}
public void accept(Person person) {
currentFrequency = frequency.merge(p.getName(), 1, Integer::sum);
if (currentFrequency > modeFrequency) {
modeName = person.getName();
modeFrequency = currentFrequency;
}
}
public Accumulator combine(Accumulator other) {
other.frequency.forEach((n, f) -> this.frequency.merge(n, f, Integer::sum));
if (this.frequency.get(other.modeName) > frequency.get(this.modeName))
modeName = other.modeName;
modeFrequency = frequency.get(modeName);
return this;
};
}
public BiConsumer<Accumulator,​Person> accumulator() {
return Accumulator::accept;
}
public Set<Collector.Characteristics> characteristics() {
return Set.of(Collector.Characteristics.CONCURRENT);
}
public BinaryOperator<Accumulator> combiner() {
return Accumulator::combine;
}
public Function<Accumulator,String> finisher() {
return Accumulator::getModeName;
}
public Supplier<Accumulator> supplier() {
return Accumulator::new;
}
}
Usage would be:
people.stream().collect(new MaxNameFinder())
which would return a string representing the most common name.

It may be possible to squeeze two passes into one using loops and Map::merge function returning the calculated frequency value immediately:
String mostCommonName = null;
int maxFreq = 0;
Map<String, Integer> freq = new HashMap<>();
for (Person p : people) {
if (freq.merge(p.getName(), 1, Integer::sum) > maxFreq) {
maxFreq = freq.get(p.getName());
mostCommonName = p.getName();
}
}
System.out.printf("Most common name '%s' occurred %d times.%n", mostCommonName, maxFreq);

Related

Collectors.groupingBy functionality and summing a field

.collect(Collectors.groupingBy(Point::getName, Collectors.summingInt(Point::getCount)));
I have a list of Point objects that I want to group by a certain key (the name field) and sum by the count field of that class. The code above does the job but returns a map of Point objects. However, I want a list of Point objects returned - not a map.
What is the cleanest way to do this with java 8 streams?
Example:
input = [pt("jack", 1), pt("jack", 1), pt("jack", 1)]
result = [pt("jack", 3)]
Thanks
You can use Collectors.toMap() with a merge function as parameter.
If you add a function to sum count fields:
public class Point {
//...
public static Point sum(Point p1, Point p2) {
return new Point(p1.getName(), p1.getCount()+p2.getCount());
}
}
Then you can use it in toMap():
List<Point> list = Collections.nCopies(10, new Point("jack", 1));
Collection<Point> output = list.stream()
.collect(Collectors.toMap(Point::getName, Function.identity(), Point::sum)) // results as Map<String, Point> {"jack", Point("jack",10)}
.values(); // to get the Point instances
System.out.println(output);
Output:
[Point [name=jack, count=10]]
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class Pointers {
private String name;
private int count;
public Pointers(String name, int count) {
this.name = name;
this.count = count;
}
public String getName() {
return name;
}
public int getCount() {
return count;
}
public void incrementCount(int amount) {
count += amount;
}
public boolean equals(Object obj) {
boolean equal = false;
if (obj instanceof Pointers) {
Pointers other = (Pointers) obj;
equal = name.equals(other.getName());
}
return equal;
}
public String toString() {
return name + count;
}
public static void main(String[] args) {
List<Pointers> list = List.of(new Pointers("Jack", 1),
new Pointers("Jack", 1),
new Pointers("Jack", 1));
Supplier<List<Pointers>> supplier = () -> new ArrayList<Pointers>();
BiConsumer<List<Pointers>, Pointers> accumulator = (l, p) -> {
if (l.contains(p)) {
Pointers elem = l.get(l.indexOf(p));
elem.incrementCount(p.getCount());
}
else {
l.add(p);
}
};
BiConsumer<List<Pointers>, List<Pointers>> combiner = (l1, l2) -> {
};
List<Pointers> lst = list.stream()
.collect(supplier, accumulator, combiner);
System.out.println(lst);
}
}
Actually, you were close. You can take the key (name) and value (point sum) and repackage it into a new Point object and return as a list. Note that by re-assiging to list, you destroy the original one which will of course be garbage collected. This approach does not require a modification of your current class.
list = list.stream()
.collect(Collectors.groupingBy(Point::getName,
Collectors.summingInt(Point::getCount)))
.entrySet().stream()
.map(e -> new Point(e.getKey(), e.getValue()))
.collect(Collectors.toList());

Java Collections - Print interstates sorted by population, city and state

I am working on a problem I came across in an interview.
Input contains Population|City|State|Interstates list
Output needs to be sorted in descending order by population first, then alphabetically by city and state, and then the interstates need to be sorted in ascending order too.
Sample input:
27|Chicago|Illinois|I-94;I-90;I-88;I-57;I-55
83|New York|New York|I-78;I-95;I-87;I-80
15|Phoenix|Arizona|I-10;I-17;I-8
15|Philadelphia|Pennsylvania|I-95;I-76
Sample output:
83
New York, New York
Interstates: I-78, I-80, I-87, I-95
27
Chicago, Illinois
Interstates: I-55, I-57, I-88, I-90, I-94
15
Philadelphia, Pennsylvania
Interstates: I-76, I-95
Phoenix, Arizona
Interstates: I-8, I-10, I-17
Here's my approach so far. I am currently stuck in the if block where I've added a comment. I am not sure if I am going in the right direction. I am looking for a hint to take the right approach here.
Scanner sc = new Scanner(System.in);
String line;
List<String> al = new ArrayList<>();
//Outer map sorts reverse by population, inner map1 sorts by city, inner
map2 sorts by state
Map<Integer, Map<String, Map<String, String>>> outerMap = new TreeMap<>
(Collections.reverseOrder());
Map<String, Map<String, String>> innerMap1 = new TreeMap<>();
Map<String, String> innerMap2 = new TreeMap<>();
while(sc.hasNextLine() && (line = sc.nextLine()).length()!=0) {
//Ignore if input contains this character
if(line.contains("#")) {
line = sc.nextLine();
}
al.add(line);
}
for(int i = 0; i < al.size(); i++) {
int outerMapKey = Integer.parseInt(al.get(i).split("\\|")[0]);
String innerMap1Key = al.get(i).split("\\|")[1];
String innerMap2Key = al.get(i).split("\\|")[2];
String value = al.get(i);
outerMap.get(outerMapKey);
if(outerMap.containsKey(outerMapKey)) {
innerMap1 = outerMap.get(outerMapKey);
/* Logic to put values in inner maps
This is going to get very convoluted, not sure if I have the
right approach
*/
}
else {
innerMap1 = new TreeMap<>();
innerMap2 = new TreeMap<>();
innerMap2.put(innerMap2Key, value);
innerMap1.put(innerMap1Key, innerMap2);
outerMap.put(outerMapKey, innerMap1);
}
}
Thank you for all your help so far. I am posting my code (working now) based on feedback here. Please take a look and suggest how it can be improved.
public static void main(String[] args) {
Map<String, List<PopulationByCityState>> map = readAndProcessInput();
printSortedOutput(map);
}
private static Map<String, List<PopulationByCityState>> readAndProcessInput() {
Map<String, List<PopulationByCityState>> map = readInput();
sortByPopulationCityAndState(map);
return map;
}
private static Map<String, List<PopulationByCityState>> readInput() {
System.out.println("Enter input:");
Scanner sc = new Scanner(System.in);
String line;
Map<String, List<PopulationByCityState>> map = new TreeMap<>(Collections.reverseOrder());
while (sc.hasNextLine() && (line = sc.nextLine()).length() != 0) {
if (line.contains("#")) {
line = sc.nextLine();
}
populateMap(line, map);
}
return map;
}
private static void populateMap(String line, Map<String, List<PopulationByCityState>> map) {
String[] s = line.split("\\|");
String[] is = s[3].split(";");
String key = s[0];
PopulationByCityState p = new PopulationByCityState();
p.setPopulation(Long.parseLong(s[0]));
p.setCity(s[1]);
p.setState(s[2]);
List<String> interstates = new ArrayList<>();
for (String aString : is) {
interstates.add(aString);
}
sortInterstates(interstates);
p.setInterstates(interstates);
if (map.containsKey(key)) {
map.get(key).add(p);
} else {
List<PopulationByCityState> al = new ArrayList<>();
al.add(p);
map.put(key, al);
}
}
private static void sortInterstates(List<String> interstates) {
Collections.sort(interstates, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
int n1 = Integer.parseInt(o1.split("-")[1]);
int n2 = Integer.parseInt(o2.split("-")[1]);
return n1 - n2;
}
});
}
private static void sortByPopulationCityAndState(Map<String, List<PopulationByCityState>> map) {
for (Map.Entry entry : map.entrySet()) {
List<PopulationByCityState> list = (List<PopulationByCityState>) entry.getValue();
Collections.sort(list, new Comparator<PopulationByCityState>() {
#Override
public int compare(PopulationByCityState o1, PopulationByCityState o2) {
int c;
c = (int) (o2.getPopulation() - o1.getPopulation());
if (c == 0) {
c = o1.getCity().compareTo(o2.getCity());
}
if (c == 0) {
c = o1.getState().compareTo(o2.getState());
}
return c;
}
});
}
}
private static void printSortedOutput(Map<String, List<PopulationByCityState>> map) {
for (Map.Entry<String, List<PopulationByCityState>> entry : map.entrySet()) {
System.out.println(entry.getKey());
System.out.println();
List<PopulationByCityState> list = entry.getValue();
for (PopulationByCityState p : list) {
System.out.println(p.getCity() + ", " + p.getState());
List<String> interstates = p.getInterstates();
System.out.print("Interstates: ");
int s = 0;
for (String is : interstates) {
s++;
System.out.print(is);
if (s != interstates.size()) {
System.out.print(", ");
}
}
System.out.println();
System.out.println();
}
}
}
Your approach relies on over complicated and not meaningful structure and also uses a Comparator that will only sort the first level of the map :
Map<Integer, Map<String, Map<String, String>>> outerMap = new TreeMap<>
(Collections.reverseOrder());
A finer approach could rely on using a class that represents each individual information that you need to represent a population for a state : PopulationForState
Here is a very simple representation of it (that is of course improvable but that should help you to understand the logic) :
public class PopulationForState{
private long population;
private String city;
private String state;
private List<String> interstates;
...
// getters
}
Add instances of them in a List and use a comparator that sorted them in descending order by population first, then alphabetically by city and state.
The interstates field may be sorted independently or directly during the sort of outer elements.
You could provide a sort method in PopulationForState, for example sortInnerStates() that sorts them in ascending order.
Personally, I would make it independently to keep the processing less coupled between.
So you could write something like :
List<PopulationForState> populationForStates = new ArrayList<>();
populationForStates.add(new PopulationForState(...));
populationForStates.add(new PopulationForState(...));
Collection.sort(populationForStates, Comparator.comparing(PopulationForState::population).reversed()
.thenComparing(PopulationForState::getCity)
.thenComparing(PopulationForState::getState);
populationForStates.stream()
.forEach(PopulationForState::sortInnerStates);
If you have a structure such the one posted in above post:
public class PopulationForState{
public long population;
public String city;
public String state;
public List<String> interstates;
//Do encapsulate
}
You can sort it with one comparator:
Collections.sort(populatisForStates, new Comparator<PopulationForState>(){
public int compare(PopulationForState first, PopulationForState scnd) {
int compare = first.population - scnd.population;
if(compare != 0) return compare;
compare = first.city.compareTo(scnd.city);
if(compare != 0) return compare;
return first.state.compareTo(scnd.state);
}
});
Sorting Interstates is similar and you just need to use Collections.sort(interstates) on each instance.

What is a better way to break java list in Map of Maps?

We have
List<persons> persons;
and we need
Map<age,Map<income,Person>> results
the way I am doing it now is :
ages.stream().forEach(a -> {
Map<Integer,Person> tmpMap = new HashMap<>();
incomes.stream().forEach(i -> {
Person p = persons.stream().filter(
u -> u.getAge() == a.getAge() &&
u.getIncome() == i.getIncome())
.findAny().orElse(null);
tmpMap.put(i.getIncome(), p);
});
returns.put(a.getAge(),tmpMap);
});
it seems like there should be a better way of doing this.
This looks like it works.
List<Person> people = Arrays.asList(
new Person("One", 21, 100),
new Person("Two", 21, 75),
new Person("Three", 42, 100),
new Person("Four", 42, 120),
new Person("Five", 9, 100)
);
Map<Integer, Map<Integer, Person>> map = people.stream()
// Gather all ages into a Map<Age,List<Person>>
.collect(Collectors.groupingBy(Person::getAge))
// Walk that transient Map.
.entrySet().stream()
.collect(Collectors.toMap(
// Key is the age.
Map.Entry::getKey,
// Value is a Map<income,person>
e -> e.getValue()
// Roll each of the same age into a Map<Income,Person>
.stream().collect(
Collectors.toMap(
// Key is income.
Person::getIncome,
// Value is the Person.
Function.identity()
))));
I roll your list into a Map<Age,List<Person>> using a groupingBy and then stream it's entrySet and collect that into the final form.
This will fail if two people of the same age have the same income because that will violate the inner Map. Use Alexander's suggestion if you are happy with the natural enhancement of generating a Map<Integer, Map<Integer, List<Person>>>.
Added
#Holger has pointed out in a comment that this can be done in a much simpler and more elegant way. Please use this form instead/
Map<Integer, Map<Integer, Person>> map2 = people.stream()
.collect(
Collectors.groupingBy(
Person::getAge,
Collectors.toMap(Person::getIncome, Function.identity())));
FYI - Here's the Person class I used. Note the equals and hashcode are implemented.
class Person {
private final String name;
private final int age;
private final int income;
public Person(String name, int age, int income) {
this.name = name;
this.age = age;
this.income = income;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getIncome() {
return income;
}
#Override
public String toString() {
return "Person{" + "name=" + name + ", age=" + age + ", income=" + income + '}';
}
#Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + Objects.hashCode(this.name);
hash = 59 * hash + this.age;
hash = 59 * hash + this.income;
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 Person other = (Person) obj;
if (this.age != other.age) {
return false;
}
if (this.income != other.income) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return true;
}
}
This should do the trick
List<person> persons = Arrays.asList(
new person(22, 1000),
new person(25, 1500),
new person(22, 2500),
new person(32, 5000)
);
Map<Integer, Map<Integer, List<person>>> map = persons.stream().collect(
groupingBy(person::getAge, groupingBy(person::getIncome))
);
System.out.println(map);
Output:
{32={5000=[person{age=32, income=5000}]}, 22={2500=[person{age=22, income=2500}], 1000=[person{age=22, income=1000}]}, 25={1500=[person{age=25, income=1500}]}}
NB: The result is not exactly what you expect as you will get a Map<Integer, Map<Integer, List<person>>> instead of Map<Integer, Map<Integer, person>> but I assume that your initial question is not correct because if you have two persons with the same age and income, you will have only one person in your map instead of two
You should take a look at Collectors.groupingBy():
List<Person> persons = new ArrayList<>();
Map<Integer, Map<Integer, List<Person>>> map = persons.stream().collect(Collectors.groupingBy(person -> person.getAge(),Collectors.groupingBy(person -> person.getIncome())));
This should do your thing.
Why not (I assume your type is person, not persons, you used both)
for (person p : persons)
{
if (!results.containsKey(p.getAge())
results.put(p.getAge(), new HashMap<income,persons>());
results.get(p.getAge()).put(p.getIncome(), p);
}
You can also implement your own collector for achieving this. With the help of Guava you can do it in one line:
Map<Integer, Map<Integer, Person>> result = persons.stream().collect(HashMap::new, (store, person) -> store.put(person.getAge(), ImmutableMap.of(person.getIncome(), person)), HashMap::putAll);
I second YaRiK; a Guava Table<Integer, Integer, Set<Person>> would work nicely here. You should use a Set<Person> to avoid collisions, like user902383 suggests. The streams API isn't always the right tool, and this looks to me like a case where a traditional iterative loop will be much easier to read.
Try this:
Table<Integer, Integer, Set<Person>> table = HashBasedTable.create();
for (Person p : persons) {
Set<Person> s = table.get(p.getAge(), p.getIncome());
if (s == null) {
s = new HashSet<>();
table.put(p.getAge(), p.getIncome(), s);
}
s.add(p);
}

Java HashMap custom Object

Example:
d1 = "the sky is blue"
d2 = "the car is blue"
Key Value
the [<d1,1>,<d2,1>]
sky [<d1,1>]
is [<d1,1>,<d2,1>]
blue [<d1,1>,<d2,1>]
car [<d2,1>]
Where:
key = String
ex:
<d1,1>
d1 = Document id
1 = How many times the word apear on file
I created a document type object with the docid variables and frequency.
public class Documento {
private final int docid;
private final int frequencia;
public Documento(int docid, int frequencia) {
this.docid = docid;
this.frequencia = frequencia;
}
public int getDocid() {
return docid;
}
public int getFrequencia() {
return frequencia;
}
#Override
public boolean equals(Object o) {
if ((o instanceof Documento) && docid == ((Documento) o).docid && frequencia == ((Documento) o).frequencia) {
return true;
}
return false;
}
And the dictionary class that is a hashmap with
public class Dicionario {
public Map<String, Documento> indice = new HashMap<>();
public void InsereDicionario(String palavra, int docid) {
int cont = indice.containsKey(palavra) ? indice.get(palavra).getFrequencia() : 0;
indice.put(palavra, new Documento(docid, cont + 1));
}
public int frequencia(String palavra) {
return indice.get(palavra).getFrequencia();
}
public void criaDicionario(String entrada) {
String[] palavras = entrada.split("\\s+");
for (int i = 0; i < palavras.length; i++) {
InsereDicionario(palavras[i], 1);
}
}
public void ListaPalavras(){
for(String key:indice.keySet()){
System.out.println("");
}
}
But what I really need the dictionary is a list of documents , and I do not know how to do this , someone could help me ?
or is there an easier way to do this ?
If you need a list of documents, why not create one? With Java8 this becomes even more convenient:
For example:
public Map<String, List<Documento>> indice = new HashMap<>();
//register new word
indice.putIfAbsent(palavra, new ArrayList<>());
//add additional occurence
indice.get(palavra).add(documento);
//get frequency
int frequencia = indice.get(palavra)
.stream()
.map(d -> d.getFrequencia())
.reduce(0, (s, i) -> s + i);
An alternative would be to use Guava's Multimap, see here
Map<String, List<Documento>>
Obviously you need to adapt the rest of the code.
For example, when you need to add something to the dictionary, if it's the first time you need to create the List with that single document, next time you need to take the already created list and add documents there.

Calculate distance to other node of same type

I'm writing a program that calculates how far 2 nodes (persons in this example) are from each other.
public class Person() {
private Set<Person> persons;
public int calculateDistanceTo(Person person) {
// how to do this
}
I tried using recursion. But that causes the program to go on endlessly.
Here's the (not working code).
public int calculateDistanceTo(Person person, int count) {
if (persons.contains(person)) return 0;
for (Person p : persons) {
count += p.calculateDistanceTo(p, ++count);
}
return count;
}
(public class Person() is not syntactically correct in Java)
Firstly, let's see what's the following loop doing:
for (Person p : persons) {
count += p.calculateDistanceTo(p, ++count);
}
Take the following relationship as an example:
In the above case, let 1 be p1, and 2 be p2, etc. P1 and P4 are not linked, so when you do:
p1.calculateDistanceTo(p4, 0);
The recursion stack would be:
calculateDistanceTo(2,1)
calculateDistanceTo(1,2)
calculateDistanceTo(2,3)
… infinite
That's why you go into an infinite loop.
To correct that:
1) You need to add a flag to a person if a person has been visited
2) The for loop should be used to determine the shortest distance from the person visited to the target person.
So essentially, it is a depth first search with some comparison to give the shortest path. (Of course, to find the shortest path in a uni-distance, we should use breadth first search. But I derived depth first search based on your given solution)
So an example would be:
public class Person {
private Set<Person> friends;
int id;
boolean visited = false;
public Person(int id) {
this.id = id;
friends = new HashSet<>();
}
public Set<Person> getFriends() {
return friends;
}
#Override
public String toString() {
return id + "";
}
public boolean equals(Object o) {
return ((Person) o).id == this.id;
}
public int calculateDistanceTo(Person person) {
this.visited = true;
if (friends.contains(person)) return 0;
int shortestDistance = Integer.MAX_VALUE / 2;
for (Person friend : friends) {
if (!friend.visited) {
int dist = friend.calculateDistanceTo(person);
if (dist < shortestDistance) {// finding the shortest distance
shortestDistance = dist;
}
}
}
return 1 + shortestDistance;
}
static Person p1 = new Person(1);
static Person p2 = new Person(2);
static Person p3 = new Person(3);
static Person p4 = new Person(4);
static Person p5 = new Person(5);
public static void main(String[] args) {
List<Person> persons = new ArrayList<>(Arrays.asList(p1, p2, p3, p4, p5));
p1.getFriends().add(p2);
p1.getFriends().add(p3);
p2.getFriends().add(p1);
p2.getFriends().add(p3);
p3.getFriends().add(p1);
p3.getFriends().add(p2);
p3.getFriends().add(p5);
p5.getFriends().add(p3);
int dist = p1.calculateDistanceTo(p4);
System.out.println(dist); // 1073741824 means no relation
for (Person p : persons) p.visited = false;// clear flag
dist = p1.calculateDistanceTo(p2);
System.out.println(dist); // 0
for (Person p : persons) p.visited = false;// clear flag
dist = p1.calculateDistanceTo(p5);
System.out.println(dist); // 1
}
}
BOND's answer is working, but there is a big Issue in implementation: The internal state of the objects will be modified during calculation. In a multi-threaded environment this will lead to unexpected behaviour. So here is my solution for this.
First the class Person (I stored a name and called the Set 'neighbours':
public class Person {
private final Set<Person> neighbours = new HashSet<>();
private final String name;
public Person(String name) {
this.name = name;
}
public void addNeighbour(Person p)
{
neighbours.add(p);
p.neighbours.add(this);
}
public int calculateDistanceTo(Person p)
{
return calculateDistanceTo(p, 0, Collections.emptySet());
}
private int calculateDistanceTo(Person person, int count, Set<Person> tried)
{
if (neighbours.contains(person))
return count;
Set<Person> nextTry = new HashSet<>(tried);
nextTry.add(this);
return neighbours.stream()
.filter(p -> !nextTry.contains(p))
.mapToInt(p -> p.calculateDistanceTo(person, count+1, nextTry))
.filter(i -> i>=0)
.findFirst()
.orElse(-1);
}
#Override
public String toString() {
return "Person{" + "name=" + name + '}';
}
}
I am creating a set of persons which I have already tried and give this set into the calculateDistanceTo method. And if two Persons are not connected, my method will return -1. So my main() method will look the following:
public static void main(String ... args)
{
Person p1 = new Person("Anton");
Person p2 = new Person("Berta");
Person p3 = new Person("Charlie");
Person p4 = new Person("Dora");
Person p5 = new Person("Emil");
p1.addNeighbour(p2);
p1.addNeighbour(p3);
p2.addNeighbour(p3);
p3.addNeighbour(p5);
System.out.println("Distance from Anton to Dora = "+p1.calculateDistanceTo(p4)); // -1
System.out.println("Distance from Anton to Berta = "+p1.calculateDistanceTo(p2)); // 0
System.out.println("Distance from Anton to Emil = "+p1.calculateDistanceTo(p5)); // 1
}
Edit As you see, it is not neccessary to implement equals() and hashCode() because we only use the references here.

Categories

Resources