diff --git a/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinks.java b/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinks.java index 073b33f1b25..30b34aebde3 100644 --- a/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinks.java +++ b/matsim/src/main/java/org/matsim/core/network/DisallowedNextLinks.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -32,16 +33,32 @@ */ public class DisallowedNextLinks { + // Actually, it could be Map>>>, as the order of the + // next links sequences does not matter. However, we choose to store them in a + // list in favor of a smaller memory footprint. private final Map>>> linkIdSequencesMap = new HashMap<>(); + /** + * Add a sequence of subsequent links to be disallowed. + * + * @param mode + * @param linkSequence sequence of links that shall not be passed after passing + * the link where this object is attached + * @return true, if linkSequence was actually added + */ public boolean addDisallowedLinkSequence(String mode, Collection> linkSequence) { - List>> sequences = this.linkIdSequencesMap.computeIfAbsent(mode, m -> new ArrayList<>()); + List>> linkSequences = this.linkIdSequencesMap.computeIfAbsent(mode, m -> new ArrayList<>()); + + boolean result = false; // prevent adding empty/duplicate link id sequences, or duplicate link ids if (!linkSequence.isEmpty() && new HashSet<>(linkSequence).size() == linkSequence.size() - && !sequences.contains(linkSequence)) { - return sequences.add(ImmutableList.copyOf(linkSequence)); + && !linkSequences.contains(linkSequence)) { + result = linkSequences.add(ImmutableList.copyOf(linkSequence)); + if (result) { // sorting is required for a.equals(b) <=> a.hashCode() == b.hashCode() + Collections.sort(linkSequences, (l, r) -> l.toString().compareTo(r.toString())); + } } - return false; + return result; } public List>> getDisallowedLinkSequences(String mode) { @@ -65,8 +82,23 @@ public void clear() { @Override public boolean equals(Object obj) { - if (obj instanceof DisallowedNextLinks dls) { - return this.linkIdSequencesMap.equals(dls.linkIdSequencesMap); + if (obj instanceof DisallowedNextLinks dnl) { + if (!this.linkIdSequencesMap.keySet().equals(dnl.linkIdSequencesMap.keySet())) { + return false; + } + for (Entry>>> entry : this.linkIdSequencesMap.entrySet()) { + String mode = entry.getKey(); + List>> linkSequences = entry.getValue(); + List>> otherLinkSequences = dnl.linkIdSequencesMap.get(mode); + // because we store next link sequences in a list, even though their order has + // no meaning, we need to ignore the order when comparing objects. + if (linkSequences.size() != otherLinkSequences.size() + || !linkSequences.containsAll(otherLinkSequences) + || !otherLinkSequences.containsAll(linkSequences)) { + return false; + } + } + return true; } return false; } diff --git a/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java b/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java index 52ebe9d5f4e..d972a906096 100644 --- a/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java +++ b/matsim/src/test/java/org/matsim/core/network/DisallowedNextLinksTest.java @@ -2,7 +2,9 @@ import java.io.File; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.Assert; import org.junit.Rule; @@ -22,40 +24,119 @@ public class DisallowedNextLinksTest { public TemporaryFolder tempFolder = new TemporaryFolder(); @Test - public void testSerialization() { + public void testEquals() { + DisallowedNextLinks dnl0 = new DisallowedNextLinks(); + DisallowedNextLinks dnl1 = new DisallowedNextLinks(); + dnl0.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1"))); + dnl1.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1"))); - DisallowedNextLinks dns0 = new DisallowedNextLinks(); - dns0.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("3"))); - dns0.addDisallowedLinkSequence("car", - List.of(Id.createLinkId("0"), Id.createLinkId("1"), Id.createLinkId("2"))); - dns0.addDisallowedLinkSequence("bike", List.of(Id.createLinkId("10"), Id.createLinkId("11"))); + Assert.assertEquals(dnl0, dnl1); + Assert.assertEquals(dnl1, dnl0); - DisallowedNextLinks.DisallowedNextLinksAttributeConverter ac = new DisallowedNextLinks.DisallowedNextLinksAttributeConverter(); + dnl1.addDisallowedLinkSequence("bike", List.of(Id.createLinkId("0"))); + + Assert.assertNotEquals(dnl0, dnl1); + Assert.assertNotEquals(dnl1, dnl0); + } + + @Test + public void testAdding() { + DisallowedNextLinks dnl = new DisallowedNextLinks(); + dnl.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1"))); + dnl.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"))); + dnl.addDisallowedLinkSequence("bike", List.of(Id.createLinkId("0"))); + + Map>>> map = Map.of( + "bike", List.of(List.of(Id.createLinkId("0"))), + "car", List.of(List.of(Id.createLinkId("0"), Id.createLinkId("1")), List.of(Id.createLinkId("0")))); + Assert.assertEquals(map, dnl.getAsMap()); + } + + @Test + public void testRemoving() { + DisallowedNextLinks dnl = new DisallowedNextLinks(); + dnl.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1"))); + dnl.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"))); + dnl.addDisallowedLinkSequence("bike", List.of(Id.createLinkId("0"))); + + dnl.removeDisallowedLinkSequences("bike"); + + Map>>> map = Map.of( + "car", List.of(List.of(Id.createLinkId("0"), Id.createLinkId("1")), List.of(Id.createLinkId("0")))); + Assert.assertEquals(map, dnl.getAsMap()); + } + + @Test + public void testNotAddingDuplicates() { + DisallowedNextLinks dnl = new DisallowedNextLinks(); + + Assert.assertTrue(dnl.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1")))); + Assert.assertFalse(dnl.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1")))); + Assert.assertFalse(dnl.addDisallowedLinkSequence("car", Collections.emptyList())); + } + + @Test + public void testNotAddingEmpty() { + DisallowedNextLinks dnl = new DisallowedNextLinks(); + + Assert.assertFalse(dnl.addDisallowedLinkSequence("car", Collections.emptyList())); + } + + @Test + public void testNotAddingSequenceWithDuplicates() { + DisallowedNextLinks dnl = new DisallowedNextLinks(); + + Assert.assertFalse(dnl.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("0")))); + } - String s = ac.convertToString(dns0); - Assert.assertEquals("{\"car\":[[\"0\",\"3\"],[\"0\",\"1\",\"2\"]],\"bike\":[[\"10\",\"11\"]]}", s); - System.out.println(s); + @Test + public void testEqualAndHashCode() { + DisallowedNextLinks dnl0 = new DisallowedNextLinks(); + dnl0.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1"))); + dnl0.addDisallowedLinkSequence("car", List.of(Id.createLinkId("4"), Id.createLinkId("5"))); + DisallowedNextLinks dnl1 = new DisallowedNextLinks(); + dnl1.addDisallowedLinkSequence("car", List.of(Id.createLinkId("4"), Id.createLinkId("5"))); + dnl1.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("1"))); + + Assert.assertEquals(dnl0, dnl1); + Assert.assertEquals(dnl0.hashCode(), dnl1.hashCode()); + } + + @Test + public void testSerialization() { + DisallowedNextLinks dnl0 = new DisallowedNextLinks(); + dnl0.addDisallowedLinkSequence("car", List.of(Id.createLinkId("0"), Id.createLinkId("3"))); + dnl0.addDisallowedLinkSequence("car", + List.of(Id.createLinkId("0"), Id.createLinkId("1"), Id.createLinkId("2"))); + dnl0.addDisallowedLinkSequence("bike", List.of(Id.createLinkId("10"), Id.createLinkId("11"))); + DisallowedNextLinks.DisallowedNextLinksAttributeConverter ac = new DisallowedNextLinks.DisallowedNextLinksAttributeConverter(); + String s = ac.convertToString(dnl0); + DisallowedNextLinks dnl1 = ac.convert(s); - DisallowedNextLinks dns1 = ac.convert(s); - Assert.assertEquals(dns0, dns1); - Assert.assertSame(dns0.getDisallowedLinkSequences("car").get(0).get(0), - dns1.getDisallowedLinkSequences("car").get(0).get(0)); + Assert.assertEquals("{\"car\":[[\"0\",\"1\",\"2\"],[\"0\",\"3\"]],\"bike\":[[\"10\",\"11\"]]}", s); + Assert.assertEquals(dnl0, dnl1); + Assert.assertEquals(dnl0.hashCode(), dnl1.hashCode()); + Assert.assertSame(dnl0.getDisallowedLinkSequences("car").get(0).get(0), + dnl1.getDisallowedLinkSequences("car").get(0).get(0)); } @Test - public void testNetworkWriting() throws IOException { + public void testNetworkWritingAndReading() throws IOException { Network n = createNetwork(); Link l1 = n.getLinks().get(Id.createLinkId("1")); - DisallowedNextLinks dnl = NetworkUtils.getOrCreateDisallowedNextLinks(l1); - dnl.addDisallowedLinkSequence("car", List.of(l1.getId())); + DisallowedNextLinks dnl0 = NetworkUtils.getOrCreateDisallowedNextLinks(l1); + dnl0.addDisallowedLinkSequence("car", List.of(l1.getId(), Id.createLinkId("2"))); File tempFile = tempFolder.newFile("network.xml"); new NetworkWriter(n).write(tempFile.toString()); Network network = NetworkUtils.createNetwork(); new MatsimNetworkReader(network).readFile(tempFile.toString()); - Assert.assertEquals(dnl, NetworkUtils.getDisallowedNextLinks(network.getLinks().get(l1.getId()))); + DisallowedNextLinks dnl1 = NetworkUtils.getDisallowedNextLinks(network.getLinks().get(l1.getId())); + Assert.assertEquals(dnl0, dnl1); + Assert.assertEquals(dnl0.hashCode(), dnl1.hashCode()); + Assert.assertSame(l1.getId(), dnl1.getDisallowedLinkSequences("car").get(0).get(0)); } static Network createNetwork() {