Optimize Map methods in ServletAttributesMap

ServletAttributesMap inherited default implementations of the size
and isEmpty methods from AbstractMap which delegates to the Set returned
by entrySet. ServletAttributesMap's entrySet method made this fairly
expensive, since it would copy the attributes to a List, then use a
Stream to build the Set. To avoid the cost, add implementations of
isEmpty / size that don't need to call entrySet at all.

Additionally, change entrySet to return a Set view that simply lazily
delegates to the underlying servlet request for iteration.

Closes gh-32189
This commit is contained in:
Patrick Strawderman
2024-02-02 10:13:13 -08:00
committed by Brian Clozel
parent c04d4da9a3
commit 0fdf759896
2 changed files with 132 additions and 10 deletions

View File

@@ -27,10 +27,12 @@ import java.security.Principal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.Part;
@@ -62,8 +64,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link DefaultServerRequest}.
* @author Arjen Poutsma
* @since 5.1
*/
class DefaultServerRequestTests {
@@ -115,6 +117,46 @@ class DefaultServerRequestTests {
assertThat(request.attribute("foo")).contains("bar");
}
@Test
void attributes() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setAttribute("foo", "bar");
servletRequest.setAttribute("baz", "qux");
DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters);
Map<String, Object> attributesMap = request.attributes();
assertThat(attributesMap).isNotEmpty();
assertThat(attributesMap).containsEntry("foo", "bar");
assertThat(attributesMap).containsEntry("baz", "qux");
assertThat(attributesMap).doesNotContainEntry("foo", "blah");
Set<Map.Entry<String, Object>> entrySet = attributesMap.entrySet();
assertThat(entrySet).isNotEmpty();
assertThat(entrySet).hasSize(attributesMap.size());
assertThat(entrySet).contains(Map.entry("foo", "bar"));
assertThat(entrySet).contains(Map.entry("baz", "qux"));
assertThat(entrySet).doesNotContain(Map.entry("foo", "blah"));
assertThat(entrySet).isUnmodifiable();
assertThat(entrySet.iterator()).toIterable().contains(Map.entry("foo", "bar"), Map.entry("baz", "qux"));
Iterator<String> attributes = servletRequest.getAttributeNames().asIterator();
Iterator<Map.Entry<String, Object>> entrySetIterator = entrySet.iterator();
while (attributes.hasNext()) {
attributes.next();
assertThat(entrySetIterator).hasNext();
entrySetIterator.next();
}
assertThat(entrySetIterator).isExhausted();
attributesMap.clear();
assertThat(attributesMap).isEmpty();
assertThat(attributesMap).hasSize(0);
assertThat(entrySet).isEmpty();
assertThat(entrySet).hasSize(0);
assertThat(entrySet.iterator()).isExhausted();
}
@Test
void params() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);