diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java index a06fd7c2611..f29f772b5d1 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java @@ -22,10 +22,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.test.ThreadContextUtilityClass; import org.apache.logging.log4j.test.junit.UsingAnyThreadContext; +import org.apache.logging.log4j.util.internal.Maps; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -115,7 +115,7 @@ void testPutAll() { assertTrue(ThreadContext.isEmpty()); assertFalse(ThreadContext.containsKey("key")); final int mapSize = 10; - final Map newMap = new HashMap<>(mapSize); + final Map newMap = Maps.newHashMap(mapSize); for (int i = 1; i <= mapSize; i++) { newMap.put("key" + i, "value" + i); } diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java index 4e3356aad31..faeb1df5449 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java @@ -20,11 +20,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.logging.log4j.test.junit.UsingThreadContextMap; import org.apache.logging.log4j.test.spi.ThreadContextMapSuite; +import org.apache.logging.log4j.util.internal.Maps; import org.apache.logging.log4j.util.PropertiesUtil; import org.junit.jupiter.api.Test; @@ -56,7 +56,7 @@ void testPutAll() { assertTrue(map.isEmpty()); assertFalse(map.containsKey("key")); final int mapSize = 10; - final Map newMap = new HashMap<>(mapSize); + final Map newMap = Maps.newHashMap(mapSize); for (int i = 1; i <= mapSize; i++) { newMap.put("key" + i, "value" + i); } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/CloseableThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/CloseableThreadContext.java index f66c8144baa..72c3828d4d7 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/CloseableThreadContext.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/CloseableThreadContext.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.logging.log4j.util.internal.Maps; /** * Adds entries to the {@link ThreadContext stack or map} and them removes them when the object is closed, e.g. as part @@ -206,7 +207,7 @@ public void close() { } private void closeMap() { - final Map valuesToReplace = new HashMap<>(originalValues.size()); + final Map valuesToReplace = Maps.newHashMap(originalValues.size()); final List keysToRemove = new ArrayList<>(originalValues.size()); for (final Map.Entry entry : originalValues.entrySet()) { final String key = entry.getKey(); diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java index 88ac55fca8c..23e0b26963d 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java @@ -24,12 +24,12 @@ import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; -import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; import org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.Lazy; +import org.apache.logging.log4j.util.internal.Maps; import org.apache.logging.log4j.util.ServiceLoaderUtil; import org.apache.logging.log4j.util.StringBuilderFormattable; import org.apache.logging.log4j.util.Strings; @@ -176,7 +176,7 @@ private static class BasicThreadInfoFactory implements ThreadInfoFactory { @Override public Map createThreadInfo() { final Map map = Thread.getAllStackTraces(); - final Map threads = new HashMap<>(map.size()); + final Map threads = Maps.newHashMap(map.size()); for (final Map.Entry entry : map.entrySet()) { threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue()); } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java index f74b10fbf2f..4da93fb18c2 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java @@ -21,9 +21,10 @@ import java.io.Serializable; import java.util.Arrays; import java.util.ConcurrentModificationException; -import java.util.HashMap; import java.util.Map; import java.util.Objects; + +import org.apache.logging.log4j.util.internal.Maps; import org.apache.logging.log4j.util.internal.SerializationUtil; /** @@ -152,7 +153,7 @@ public boolean containsKey(final String key) { @Override public Map toMap() { - final Map result = new HashMap<>(size()); + final Map result = Maps.newHashMap(size()); for (int i = 0; i < size(); i++) { final Object value = getValueAt(i); result.put(getKeyAt(i), value == null ? null : String.valueOf(value)); diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/internal/Maps.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/internal/Maps.java new file mode 100644 index 00000000000..b26ec8580d5 --- /dev/null +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/internal/Maps.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.util.internal; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +public final class Maps { + private Maps() {} + + /** + * Calculate initial capacity from expected size and default load factor (0.75). + */ + private static int calculateCapacity(int numMappings) { + return (int) Math.ceil(numMappings / 0.75d); + } + + /** + * Creates a new, empty HashMap suitable for the expected number of mappings. + * The returned map is large enough so that the expected number of mappings can be + * added without resizing the map. + * + * This is essentially a backport of HashMap.newHashMap which was added in JDK19. + */ + public static HashMap newHashMap(int numMappings) { + return new HashMap<>(calculateCapacity(numMappings)); + } + + /** + * Creates a new, empty LinkedHashMap suitable for the expected number of mappings. + * The returned map is large enough so that the expected number of mappings can be + * added without resizing the map. + * + * This is essentially a backport of LinkedHashMap.newLinkedHashMap which was added in JDK19. + */ + public static LinkedHashMap newLinkedHashMap(int numMappings) { + return new LinkedHashMap<>(calculateCapacity(numMappings)); + } +} diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/context/internal/GarbageFreeSortedArrayThreadContextMapTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/context/internal/GarbageFreeSortedArrayThreadContextMapTest.java index 54a4a23a15f..44bc088e5c9 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/context/internal/GarbageFreeSortedArrayThreadContextMapTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/context/internal/GarbageFreeSortedArrayThreadContextMapTest.java @@ -20,9 +20,10 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.HashMap; import java.util.Map; import java.util.Properties; + +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.spi.ThreadContextMap; import org.apache.logging.log4j.test.spi.ThreadContextMapSuite; import org.apache.logging.log4j.util.PropertiesUtil; @@ -52,7 +53,7 @@ void testPutAll() { assertTrue(map.isEmpty()); assertFalse(map.containsKey("key")); final int mapSize = 10; - final Map newMap = new HashMap<>(mapSize); + final Map newMap = Maps.newHashMap(mapSize); for (int i = 1; i <= mapSize; i++) { newMap.put("key" + i, "value" + i); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java index 9595e00b5a8..6f6e13a370b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.core.appender; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.logging.log4j.core.Appender; @@ -28,6 +27,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.config.plugins.PluginNode; import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.status.StatusLogger; /** @@ -60,7 +60,7 @@ public AppenderSet build() { LOGGER.error("No children node in AppenderSet {}", this); return null; } - final Map map = new HashMap<>(children.size()); + final Map map = Maps.newHashMap(children.size()); for (final Node childNode : children) { final String key = childNode.getAttributes().get("name"); if (key == null) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/LoggerNameLevelRewritePolicy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/LoggerNameLevelRewritePolicy.java index c2d6a4e5044..7a44d87c44c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/LoggerNameLevelRewritePolicy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/LoggerNameLevelRewritePolicy.java @@ -18,7 +18,6 @@ import static org.apache.logging.log4j.util.Strings.toRootUpperCase; -import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.Core; @@ -29,6 +28,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.util.KeyValuePair; +import org.apache.logging.log4j.core.util.internal.Maps; /** * Rewrites log event levels for a given logger name. @@ -45,11 +45,9 @@ public class LoggerNameLevelRewritePolicy implements RewritePolicy { /** * Creates a policy to rewrite levels for a given logger name. * - * @param loggerNamePrefix - * The logger name prefix for events to rewrite; all event logger names that start with this string will be - * rewritten. - * @param levelPairs - * The levels to rewrite, the key is the source level, the value the target level. + * @param loggerNamePrefix The logger name prefix for events to rewrite; all event logger names that start with this string will be + * rewritten. + * @param levelPairs The levels to rewrite, the key is the source level, the value the target level. * @return a new LoggerNameLevelRewritePolicy */ @PluginFactory @@ -58,7 +56,7 @@ public static LoggerNameLevelRewritePolicy createPolicy( @PluginAttribute("logger") final String loggerNamePrefix, @PluginElement("KeyValuePair") final KeyValuePair[] levelPairs) { // @formatter:on - final Map newMap = new HashMap<>(levelPairs.length); + final Map newMap = Maps.newHashMap(levelPairs.length); for (final KeyValuePair keyValuePair : levelPairs) { newMap.put(getLevel(keyValuePair.getKey()), getLevel(keyValuePair.getValue())); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/PropertiesRewritePolicy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/PropertiesRewritePolicy.java index 5e44a6bc1e0..a2f7bf22202 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/PropertiesRewritePolicy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rewrite/PropertiesRewritePolicy.java @@ -17,7 +17,6 @@ package org.apache.logging.log4j.core.appender.rewrite; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.logging.log4j.Logger; @@ -31,6 +30,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.impl.ContextDataFactory; import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.StringMap; @@ -55,7 +55,7 @@ public final class PropertiesRewritePolicy implements RewritePolicy { private PropertiesRewritePolicy(final Configuration config, final List props) { this.config = config; - this.properties = new HashMap<>(props.size()); + this.properties = Maps.newHashMap(props.size()); for (final Property property : props) { final Boolean interpolate = Boolean.valueOf(property.getValue().contains("${")); properties.put(property, interpolate); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MapFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MapFilter.java index 4e4753906e4..86673d0792f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MapFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MapFilter.java @@ -32,6 +32,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.util.KeyValuePair; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; @@ -286,7 +287,7 @@ protected boolean isAnd() { /** @deprecated use {@link #getStringMap()} instead */ @Deprecated protected Map> getMap() { - final Map> result = new HashMap<>(map.size()); + final Map> result = Maps.newHashMap(map.size()); map.forEach((key, value) -> result.put(key, (List) value)); return result; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ListOfMapEntryDeserializer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ListOfMapEntryDeserializer.java index 835ea72ce38..c7daf491993 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ListOfMapEntryDeserializer.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ListOfMapEntryDeserializer.java @@ -21,6 +21,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.apache.logging.log4j.core.util.internal.Maps; + import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -46,7 +48,7 @@ public Map deserialize(final JsonParser jp, final Deserializatio new TypeReference>() { // empty }); - final HashMap map = new HashMap<>(list.size()); + final HashMap map = Maps.newHashMap(list.size()); for (final MapEntry mapEntry : list) { map.put(mapEntry.getKey(), mapEntry.getValue()); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java index 4de5d210b8f..6063ad6c30f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractJacksonLayout.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.io.Writer; import java.nio.charset.Charset; -import java.util.LinkedHashMap; import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; @@ -42,6 +41,7 @@ import org.apache.logging.log4j.core.time.Instant; import org.apache.logging.log4j.core.util.KeyValuePair; import org.apache.logging.log4j.core.util.StringBuilderWriter; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.Strings; @@ -356,7 +356,7 @@ protected Object wrapLogEvent(final LogEvent event) { private Map resolveAdditionalFields(final LogEvent logEvent) { // Note: LinkedHashMap retains order - final Map additionalFieldsMap = new LinkedHashMap<>(additionalFields.length); + final Map additionalFieldsMap = Maps.newLinkedHashMap(additionalFields.length); final StrSubstitutor strSubstitutor = configuration.getStrSubstitutor(); // Go over each field diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java index 256ce20f28b..8a7b6e3f20f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java @@ -51,6 +51,7 @@ import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter; import org.apache.logging.log4j.core.util.NetUtils; import org.apache.logging.log4j.core.util.Patterns; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MessageCollectionMessage; import org.apache.logging.log4j.message.StructuredDataCollectionMessage; @@ -219,7 +220,7 @@ private Rfc5424Layout( private Map createFieldFormatters( final LoggerFields[] loggerFields, final Configuration config) { - final Map sdIdMap = new HashMap<>(loggerFields == null ? 0 : loggerFields.length); + final Map sdIdMap = Maps.newHashMap(loggerFields == null ? 0 : loggerFields.length); if (loggerFields != null) { for (final LoggerFields loggerField : loggerFields) { final StructuredDataId key = loggerField.getSdId() == null ? mdcSdId : loggerField.getSdId(); @@ -908,7 +909,7 @@ public FieldFormatter(final Map> fieldMap, final } public StructuredDataElement format(final LogEvent event) { - final Map map = new HashMap<>(delegateMap.size()); + final Map map = Maps.newHashMap(delegateMap.size()); for (final Map.Entry> entry : delegateMap.entrySet()) { final StringBuilder buffer = new StringBuilder(); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java index 40aaa7da3e0..f1dca295a41 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MapLookup.java @@ -16,11 +16,11 @@ */ package org.apache.logging.log4j.core.lookup; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.util.Strings; @@ -62,10 +62,6 @@ static Map initMap(final String[] srcArgs, final Map newMap(final int initialCapacity) { - return new HashMap<>(initialCapacity); - } - /** * An application's {@code public static main(String[])} method calls this method to make its main arguments * available for lookup with the prefix {@code main}. @@ -102,14 +98,14 @@ static Map toMap(final List args) { return null; } final int size = args.size(); - return initMap(args.toArray(Strings.EMPTY_ARRAY), newMap(size)); + return initMap(args.toArray(Strings.EMPTY_ARRAY), Maps.newHashMap(size)); } static Map toMap(final String[] args) { if (args == null) { return null; } - return initMap(args, newMap(args.length)); + return initMap(args, Maps.newHashMap(args.length)); } protected Map getMap() { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/PropertiesLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/PropertiesLookup.java index a0390681f1d..5279fe065a8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/PropertiesLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/PropertiesLookup.java @@ -17,11 +17,11 @@ package org.apache.logging.log4j.core.lookup; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.util.internal.Maps; /** * A lookup designed for {@code Properties} defined in the configuration. This is similar @@ -102,7 +102,7 @@ private static Map createConfigurationPrope // The raw property values must be used without the substitution handled by the plugin framework // which calls this method, otherwise we risk re-interpolating through unexpected data. // The PropertiesLookup is unique in that results from this lookup support recursive evaluation. - final Map result = new HashMap<>(props.length); + final Map result = Maps.newHashMap(props.length); for (Property property : props) { result.put(property.getName(), new ConfigurationPropertyResult(property.getRawValue())); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java index 5c713f340e7..c3489e68c5c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationAware; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.Strings; @@ -472,7 +473,7 @@ public static String replace(final Object source, final Properties valueProperti } private static Map toTypeSafeMap(final Properties properties) { - final Map map = new HashMap<>(properties.size()); + final Map map = Maps.newHashMap(properties.size()); for (final String name : properties.stringPropertyNames()) { map.put(name, properties.getProperty(name)); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/message/ExtendedThreadInfoFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/message/ExtendedThreadInfoFactory.java index 7f8ccb0f508..cbf7790395a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/message/ExtendedThreadInfoFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/message/ExtendedThreadInfoFactory.java @@ -22,8 +22,9 @@ import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.lang.reflect.Method; -import java.util.HashMap; import java.util.Map; + +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory; import org.apache.logging.log4j.message.ThreadInformation; @@ -51,7 +52,7 @@ public Map createThreadInfo() { final ThreadMXBean bean = ManagementFactory.getThreadMXBean(); final ThreadInfo[] array = bean.dumpAllThreads(true, true); - final Map threads = new HashMap<>(array.length); + final Map threads = Maps.newHashMap(array.length); for (final ThreadInfo info : array) { threads.put(new ExtendedThreadInformation(info), info.getStackTrace()); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java index 072dfeb87ff..373d25694c5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/JAnsiTextRenderer.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Objects; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.status.StatusLogger; /** @@ -104,7 +105,7 @@ private static Map.Entry entry(final String name, final AnsiEsca @SafeVarargs private static Map ofEntries(final Map.Entry... entries) { - final Map map = new HashMap<>(entries.length); + final Map map = Maps.newHashMap(entries.length); for (final Map.Entry entry : entries) { map.put(entry.getKey(), entry.getValue()); } @@ -209,7 +210,7 @@ public JAnsiTextRenderer(final String[] formats, final Map defau final String predefinedStyle = map.remove("Style"); // Create style map - final Map styleMap = new HashMap<>(map.size() + defaultStyleMap.size()); + final Map styleMap = Maps.newHashMap(map.size() + defaultStyleMap.size()); defaultStyleMap.forEach((k, v) -> styleMap.put(toRootUpperCase(k), v)); if (predefinedStyle != null) { final Map predefinedMap = PREFEDINED_STYLE_MAPS.get(predefinedStyle); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java index 3f2856340fd..3afba5a7d04 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java @@ -22,7 +22,6 @@ import java.io.File; import java.time.Instant; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -38,6 +37,7 @@ import org.apache.logging.log4j.core.AbstractLifeCycle; import org.apache.logging.log4j.core.config.ConfigurationFileWatcher; import org.apache.logging.log4j.core.config.ConfigurationScheduler; +import org.apache.logging.log4j.core.util.internal.Maps; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.ServiceLoaderUtil; @@ -154,7 +154,7 @@ public void checkFiles() { * @since 2.11.2 */ public Map getConfigurationWatchers() { - final Map map = new HashMap<>(watchers.size()); + final Map map = Maps.newHashMap(watchers.size()); for (final Map.Entry entry : watchers.entrySet()) { map.put(entry.getKey(), entry.getValue().getWatcher()); } @@ -182,7 +182,7 @@ public int getIntervalSeconds() { */ @Deprecated public Map getWatchers() { - final Map map = new HashMap<>(watchers.size()); + final Map map = Maps.newHashMap(watchers.size()); for (Map.Entry entry : watchers.entrySet()) { if (entry.getValue().getWatcher() instanceof ConfigurationFileWatcher) { map.put(entry.getKey().getFile(), (FileWatcher) entry.getValue().getWatcher()); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/Maps.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/Maps.java new file mode 100644 index 00000000000..34c50200287 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/Maps.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.core.util.internal; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +public final class Maps { + private Maps() {} + + /** + * Calculate initial capacity from expected size and default load factor (0.75). + */ + private static int calculateCapacity(int numMappings) { + return (int) Math.ceil(numMappings / 0.75d); + } + + /** + * Creates a new, empty HashMap suitable for the expected number of mappings. + * The returned map is large enough so that the expected number of mappings can be + * added without resizing the map. + * + * This is essentially a backport of HashMap.newHashMap which was added in JDK19. + */ + public static HashMap newHashMap(int numMappings) { + return new HashMap<>(calculateCapacity(numMappings)); + } + + /** + * Creates a new, empty LinkedHashMap suitable for the expected number of mappings. + * The returned map is large enough so that the expected number of mappings can be + * added without resizing the map. + * + * This is essentially a backport of LinkedHashMap.newLinkedHashMap which was added in JDK19. + */ + public static LinkedHashMap newLinkedHashMap(int numMappings) { + return new LinkedHashMap<>(calculateCapacity(numMappings)); + } +} diff --git a/log4j-perf-test/src/main/java/org/apache/logging/log4j/perf/nogc/OpenHashStringMap.java b/log4j-perf-test/src/main/java/org/apache/logging/log4j/perf/nogc/OpenHashStringMap.java index 5e1f3f4fc59..5b3e17e5cf2 100644 --- a/log4j-perf-test/src/main/java/org/apache/logging/log4j/perf/nogc/OpenHashStringMap.java +++ b/log4j-perf-test/src/main/java/org/apache/logging/log4j/perf/nogc/OpenHashStringMap.java @@ -22,10 +22,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.ConcurrentModificationException; -import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.spi.ThreadContextMap; import org.apache.logging.log4j.util.BiConsumer; +import org.apache.logging.log4j.util.internal.Maps; import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.StringMap; import org.apache.logging.log4j.util.TriConsumer; @@ -233,7 +233,7 @@ private void tryCapacity(final long capacity) { @Override public Map toMap() { - final Map result = new HashMap<>(size); + final Map result = Maps.newHashMap(size); forEach(COPY_INTO_MAP, result); return result; }