This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 27f4857248 FilteredMap/MapBuilder improvements
27f4857248 is described below

commit 27f48572485699737aef21bbb7917b7c33f4cccc
Author: James Bognar <[email protected]>
AuthorDate: Sun Dec 14 15:16:05 2025 -0500

    FilteredMap/MapBuilder improvements
---
 .../org/apache/juneau/junit/bct/Listifiers.java    |   2 +
 .../juneau/commons/collections/FilteredList.java   | 648 ++++++++++++++
 .../juneau/commons/collections/FilteredSet.java    | 595 +++++++++++++
 .../juneau/commons/collections/ListBuilder.java    |  51 +-
 .../juneau/commons/collections/MapBuilder.java     | 229 ++++-
 .../juneau/commons/collections/SetBuilder.java     |  51 +-
 .../juneau/commons/utils/CollectionUtils.java      |   4 +-
 .../org/apache/juneau/commons/utils/Utils.java     |  12 +
 .../apache/juneau/bean/openapi3/Callback_Test.java |   2 +-
 .../juneau/bean/openapi3/Components_Test.java      |   2 +-
 .../apache/juneau/bean/openapi3/OpenApi_Test.java  |   2 +-
 .../juneau/bean/openapi3/Operation_Test.java       |   2 +-
 .../juneau/bean/openapi3/Parameter_Test.java       |   2 +-
 .../commons/collections/FilteredList_Test.java     | 968 +++++++++++++++++++++
 .../commons/collections/FilteredSet_Test.java      | 948 ++++++++++++++++++++
 .../commons/collections/MapBuilder_Test.java       | 241 ++++-
 16 files changed, 3680 insertions(+), 79 deletions(-)

diff --git 
a/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/Listifiers.java
 
b/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/Listifiers.java
index 459e1468c2..35bd0045dd 100644
--- 
a/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/Listifiers.java
+++ 
b/juneau-core/juneau-bct/src/main/java/org/apache/juneau/junit/bct/Listifiers.java
@@ -135,6 +135,7 @@ public class Listifiers {
        public static Listifier<Collection> collectionListifier() {
                return (bc, collection) -> {
                        if (collection instanceof Set && ! (collection 
instanceof SortedSet) && ! (collection instanceof LinkedHashSet)) {
+                               // TODO - This is too unreliable.
                                var collection2 = new 
TreeSet<>(flexibleComparator(bc));
                                collection2.addAll(collection);
                                collection = collection2;
@@ -314,6 +315,7 @@ public class Listifiers {
        public static Listifier<Map> mapListifier() {
                return (bc, map) -> {
                        if (! (map instanceof SortedMap) && ! (map instanceof 
LinkedHashMap)) {
+                               // TODO - This is too unreliable.
                                var map2 = new 
TreeMap<>(flexibleComparator(bc));
                                map2.putAll(map);
                                map = map2;
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/FilteredList.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/FilteredList.java
new file mode 100644
index 0000000000..f5ceac29e2
--- /dev/null
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/FilteredList.java
@@ -0,0 +1,648 @@
+/*
+ * 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.juneau.commons.collections;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+import static org.apache.juneau.commons.utils.ThrowableUtils.*;
+import static org.apache.juneau.commons.utils.Utils.*;
+
+import java.util.*;
+import java.util.function.*;
+
+/**
+ * A list wrapper that filters elements based on a {@link Predicate} when they 
are added.
+ *
+ * <p>
+ * This class wraps an underlying list and applies a filter to determine 
whether elements should be added.
+ * Only elements that pass the filter (i.e., the predicate returns 
<jk>true</jk>) are actually stored in
+ * the underlying list.
+ *
+ * <h5 class='section'>Features:</h5>
+ * <ul class='spaced-list'>
+ *     <li><b>Flexible Filtering:</b> Use any {@link Predicate} to filter 
elements based on any criteria
+ *     <li><b>Optional Filter:</b> Filter is optional when using the builder - 
defaults to accepting all elements
+ *     <li><b>Multiple Filters:</b> Can combine multiple filters using AND 
logic
+ *     <li><b>Custom List Types:</b> Works with any list implementation via 
the builder's <c>inner</c> method
+ *     <li><b>Type Conversion:</b> Supports automatic type conversion via 
element function
+ *     <li><b>Convenience Methods:</b> Provides {@link #add(Object)}, {@link 
#addAll(Collection)}, and {@link #addAny(Object...)} for easy element addition
+ *     <li><b>Transparent Interface:</b> Implements the full {@link List} 
interface, so it can be used anywhere a list is expected
+ *     <li><b>Filter on Add:</b> Filtering happens when elements are added via 
{@link #add(Object)}, {@link #addAll(Collection)}, etc.
+ * </ul>
+ *
+ * <h5 class='section'>Usage:</h5>
+ * <p class='bjava'>
+ *     <jc>// Create a filtered list that only accepts non-null values</jc>
+ *     FilteredList&lt;String&gt; <jv>list</jv> = FilteredList
+ *             .<jsm>create</jsm>(String.<jk>class</jk>)
+ *             .filter(v -&gt; v != <jk>null</jk>)
+ *             .build();
+ *
+ *     <jv>list</jv>.add(<js>"value1"</js>);  <jc>// Added</jc>
+ *     <jv>list</jv>.add(<jk>null</jk>);      <jc>// Filtered out</jc>
+ *     <jv>list</jv>.add(<js>"value3"</js>);  <jc>// Added</jc>
+ *
+ *     <jc>// list now contains: ["value1", "value3"]</jc>
+ * </p>
+ *
+ * <h5 class='section'>Example - Custom List Type:</h5>
+ * <p class='bjava'>
+ *     <jc>// Create a filtered ArrayList that only accepts positive 
numbers</jc>
+ *     FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+ *             .<jsm>create</jsm>(Integer.<jk>class</jk>)
+ *             .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+ *             .inner(<jk>new</jk> ArrayList&lt;&gt;())
+ *             .build();
+ *
+ *     <jv>list</jv>.add(5);   <jc>// Added</jc>
+ *     <jv>list</jv>.add(-1);  <jc>// Filtered out</jc>
+ *     <jv>list</jv>.add(10);  <jc>// Added</jc>
+ * </p>
+ *
+ * <h5 class='section'>Behavior Notes:</h5>
+ * <ul class='spaced-list'>
+ *     <li>Filtering is applied to all element addition methods: {@link 
#add(Object)}, {@link #addAll(Collection)}, etc.
+ *     <li>If the filter returns <jk>false</jk>, the element is silently 
ignored (not added)
+ *     <li>When {@link #add(Object)} filters out an element, it returns 
<jk>false</jk> (element was not added)
+ *     <li>The filter is not applied when reading from the list (e.g., {@link 
#get(int)}, {@link #contains(Object)})
+ *     <li>All other list operations behave as expected on the underlying list
+ *     <li>The underlying list type is determined by the <c>inner</c> method 
(defaults to {@link ArrayList})
+ * </ul>
+ *
+ * <h5 class='section'>Thread Safety:</h5>
+ * <p>
+ * This class is not thread-safe unless the underlying list is thread-safe. If 
thread safety is required,
+ * use a thread-safe list type (e.g., {@link 
java.util.concurrent.CopyOnWriteArrayList}) via the <c>inner</c> method.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ *     <li class='link'><a class="doclink" 
href="../../../../../index.html#juneau-commons">Overview &gt; juneau-commons</a>
+ * </ul>
+ *
+ * @param <E> The element type.
+ */
+public class FilteredList<E> extends AbstractList<E> {
+
+       /**
+        * Builder for creating {@link FilteredList} instances.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .inner(<jk>new</jk> ArrayList&lt;&gt;())
+        *              .build();
+        * </p>
+        *
+        * @param <E> The element type.
+        */
+       public static class Builder<E> {
+               private Predicate<E> filter = v -> true;
+               private List<E> inner;
+               private Class<E> elementType;
+               private Function<Object,E> elementFunction;
+
+               /**
+                * Sets the filter predicate that determines whether elements 
should be added.
+                *
+                * <p>
+                * The predicate receives the element value. If it returns 
<jk>true</jk>,
+                * the element is added to the list. If it returns 
<jk>false</jk>, the element is silently ignored.
+                *
+                * <p>
+                * This method is optional. If not called, the list will accept 
all elements (defaults to <c>v -&gt; true</c>).
+                *
+                * <p>
+                * This method can be called multiple times. When called 
multiple times, all filters are combined
+                * using AND logic - an element must pass all filters to be 
added to the list.
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      <jc>// Filter out null values</jc>
+                *      Builder&lt;String&gt; <jv>b</jv> = 
FilteredList.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk>);
+                *
+                *      <jc>// Filter based on value</jc>
+                *      Builder&lt;Integer&gt; <jv>b2</jv> = 
FilteredList.<jsm>create</jsm>(Integer.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v 
&gt; 0);
+                *
+                *      <jc>// Multiple filters combined with AND</jc>
+                *      Builder&lt;Integer&gt; <jv>b3</jv> = 
FilteredList.<jsm>create</jsm>(Integer.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk>)           
<jc>// First filter</jc>
+                *              .filter(v -&gt; v &gt; 0)                    
<jc>// Second filter (ANDed with first)</jc>
+                *              .filter(v -&gt; v &lt; 100);                  
<jc>// Third filter (ANDed with previous)</jc>
+                * </p>
+                *
+                * @param value The filter predicate. Must not be <jk>null</jk>.
+                * @return This object for method chaining.
+                */
+               public Builder<E> filter(Predicate<E> value) {
+                       Predicate<E> newFilter = assertArgNotNull("value", 
value);
+                       if (filter == null)
+                               filter = newFilter;
+                       else
+                               filter = filter.and(newFilter);
+                       return this;
+               }
+
+               /**
+                * Sets the underlying list instance that will store the 
filtered elements.
+                *
+                * <p>
+                * If not specified, a new {@link ArrayList} will be created 
during {@link #build()}.
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      <jc>// Use a LinkedList</jc>
+                *      Builder&lt;String&gt; <jv>b</jv> = 
FilteredList.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .inner(<jk>new</jk> LinkedList&lt;&gt;());
+                *
+                *      <jc>// Use a CopyOnWriteArrayList for thread safety</jc>
+                *      Builder&lt;String&gt; <jv>b2</jv> = 
FilteredList.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .inner(<jk>new</jk> 
CopyOnWriteArrayList&lt;&gt;());
+                *
+                *      <jc>// Use an existing list</jc>
+                *      List&lt;String&gt; <jv>existing</jv> = <jk>new</jk> 
ArrayList&lt;&gt;();
+                *      Builder&lt;String&gt; <jv>b3</jv> = 
FilteredList.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .inner(<jv>existing</jv>);
+                * </p>
+                *
+                * @param value The underlying list instance. Must not be 
<jk>null</jk>.
+                * @return This object for method chaining.
+                */
+               public Builder<E> inner(List<E> value) {
+                       inner = assertArgNotNull("value", value);
+                       return this;
+               }
+
+               /**
+                * Sets the function to use for converting elements when using 
{@link FilteredList#add(Object)}.
+                *
+                * <p>
+                * If specified, elements passed to {@link 
FilteredList#add(Object)} will be converted using
+                * this function before being added to the list. If not 
specified, elements must already be of the
+                * correct type (or <c>Object.class</c> is used if types were 
not specified, which accepts any type).
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+                *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v 
&gt; 0)
+                *              .elementFunction(o -&gt; 
Integer.parseInt(o.toString()))
+                *              .build();
+                *
+                *      <jv>list</jv>.add(<js>"123"</js>);  <jc>// Element will 
be converted from String to Integer</jc>
+                * </p>
+                *
+                * @param value The element conversion function. Can be 
<jk>null</jk>.
+                * @return This object for method chaining.
+                */
+               public Builder<E> elementFunction(Function<Object,E> value) {
+                       elementFunction = value;
+                       return this;
+               }
+
+               /**
+                * Builds a new {@link FilteredList} instance.
+                *
+                * <p>
+                * If {@link #filter(Predicate)} was not called, the list will 
accept all elements
+                * (defaults to <c>v -&gt; true</c>).
+                *
+                * @return A new filtered list instance.
+                */
+               public FilteredList<E> build() {
+                       List<E> list = inner != null ? inner : new 
ArrayList<>();
+                       return new FilteredList<>(filter, list, elementType, 
elementFunction);
+               }
+       }
+
+       /**
+        * Creates a new builder for constructing a filtered list.
+        *
+        * @param <E> The element type.
+        * @param elementType The element type class. Must not be <jk>null</jk>.
+        * @return A new builder.
+        */
+       public static <E> Builder<E> create(Class<E> elementType) {
+               assertArgNotNull("elementType", elementType);
+               var builder = new Builder<E>();
+               builder.elementType = elementType;
+               return builder;
+       }
+
+       /**
+        * Creates a new builder for constructing a filtered list with generic 
types.
+        *
+        * <p>
+        * This is a convenience method that creates a builder without 
requiring explicit type class parameters.
+        * The generic types must be explicitly specified using the diamond 
operator syntax.
+        *
+        * <p>
+        * When using this method, type checking is effectively disabled (using 
<c>Object.class</c> as the type),
+        * allowing any element types to be added. However, the generic type 
parameter should still be
+        * specified for type safety.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Explicitly specify generic types using diamond 
operator</jc>
+        *      <jk>var</jk> <jv>list</jv> = FilteredList
+        *              .&lt;String&gt;<jsm>create</jsm>()
+        *              .filter(<jv>v</jv> -&gt; <jv>v</jv> != <jk>null</jk>)
+        *              .build();
+        * </p>
+        *
+        * @param <E> The element type.
+        * @return A new builder.
+        */
+       @SuppressWarnings("unchecked")
+       public static <E> Builder<E> create() {
+               var builder = new Builder<E>();
+               builder.elementType = (Class<E>)Object.class;
+               return builder;
+       }
+       private final Predicate<E> filter;
+       private final List<E> list;
+       private final Class<E> elementType;
+       private final Function<Object,E> elementFunction;
+
+       /**
+        * Constructor.
+        *
+        * @param filter The filter predicate. Can be <jk>null</jk> (if null, 
all elements are accepted).
+        * @param list The underlying list. Must not be <jk>null</jk>.
+        * @param elementType The element type. Must not be <jk>null</jk> (use 
<c>Object.class</c> to disable type checking).
+        * @param elementFunction The element conversion function, or 
<jk>null</jk> if not specified.
+        */
+       protected FilteredList(Predicate<E> filter, List<E> list, Class<E> 
elementType, Function<Object,E> elementFunction) {
+               this.filter = assertArgNotNull("filter", filter);
+               this.list = assertArgNotNull("list", list);
+               this.elementType = assertArgNotNull("elementType", elementType);
+               this.elementFunction = elementFunction;
+       }
+
+       /**
+        * Appends the specified element to the end of this list, if it passes 
the filter.
+        *
+        * <p>
+        * If the element passes the filter, it is added to the list and this 
method returns <jk>true</jk>.
+        *
+        * <p>
+        * If the element is filtered out, it is not added to the list and this 
method returns <jk>false</jk>.
+        *
+        * @param e The element to be appended to this list.
+        * @return <jk>true</jk> if the element was added, <jk>false</jk> if it 
was filtered out.
+        */
+       @Override
+       public boolean add(E e) {
+               if (filter.test(e))
+                       return list.add(e);
+               return false;
+       }
+
+       @Override
+       public void add(int index, E element) {
+               if (filter.test(element))
+                       list.add(index, element);
+       }
+
+       @Override
+       public boolean addAll(Collection<? extends E> c) {
+               boolean modified = false;
+               for (var e : c) {
+                       if (filter.test(e)) {
+                               list.add(e);
+                               modified = true;
+                       }
+               }
+               return modified;
+       }
+
+       @Override
+       public boolean addAll(int index, Collection<? extends E> c) {
+               int insertIndex = index;
+               boolean modified = false;
+               for (var e : c) {
+                       if (filter.test(e)) {
+                               list.add(insertIndex++, e);
+                               modified = true;
+                       }
+               }
+               return modified;
+       }
+
+       /**
+        * Tests whether the specified element would pass the filter and be 
added to this list.
+        *
+        * <p>
+        * This method can be used to check if an element would be accepted by 
the filter without actually
+        * adding it to the list. This is useful for validation, debugging, or 
pre-checking elements before
+        * attempting to add them.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .build();
+        *
+        *      <jk>if</jk> (<jv>list</jv>.wouldAccept(5)) {
+        *              <jv>list</jv>.add(5);  <jc>// Will be added</jc>
+        *      }
+        * </p>
+        *
+        * @param element The element to test.
+        * @return <jk>true</jk> if the element would be added to the list, 
<jk>false</jk> if it would be filtered out.
+        */
+       public boolean wouldAccept(E element) {
+               return filter.test(element);
+       }
+
+       /**
+        * Returns the filter predicate used by this list.
+        *
+        * <p>
+        * This method provides access to the filter for debugging, inspection, 
or advanced use cases.
+        * The returned predicate is the combined filter used internally by 
this list. If multiple filters
+        * were set via the builder, they are combined using AND logic.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .build();
+        *
+        *      Predicate&lt;Integer&gt; <jv>filter</jv> = 
<jv>list</jv>.getFilter();
+        *      <jc>// Use filter for other purposes</jc>
+        * </p>
+        *
+        * @return The filter predicate. Never <jk>null</jk>.
+        */
+       public Predicate<E> getFilter() {
+               return filter;
+       }
+
+       @Override
+       public E get(int index) {
+               return list.get(index);
+       }
+
+       @Override
+       public E set(int index, E element) {
+               if (filter.test(element))
+                       return list.set(index, element);
+               return list.get(index);  // Return existing element if filtered 
out
+       }
+
+       @Override
+       public E remove(int index) {
+               return list.remove(index);
+       }
+
+       @Override
+       public boolean remove(Object o) {
+               return list.remove(o);
+       }
+
+       @Override
+       public boolean removeAll(Collection<?> c) {
+               return list.removeAll(c);
+       }
+
+       @Override
+       public boolean retainAll(Collection<?> c) {
+               return list.retainAll(c);
+       }
+
+       @Override
+       public void clear() {
+               list.clear();
+       }
+
+       @Override
+       public int size() {
+               return list.size();
+       }
+
+       @Override
+       public boolean isEmpty() {
+               return list.isEmpty();
+       }
+
+       @Override
+       public boolean contains(Object o) {
+               return list.contains(o);
+       }
+
+       @Override
+       public boolean containsAll(Collection<?> c) {
+               return list.containsAll(c);
+       }
+
+       @Override
+       public int indexOf(Object o) {
+               return list.indexOf(o);
+       }
+
+       @Override
+       public int lastIndexOf(Object o) {
+               return list.lastIndexOf(o);
+       }
+
+       @Override
+       public ListIterator<E> listIterator() {
+               return list.listIterator();
+       }
+
+       @Override
+       public ListIterator<E> listIterator(int index) {
+               return list.listIterator(index);
+       }
+
+       @Override
+       public List<E> subList(int fromIndex, int toIndex) {
+               return list.subList(fromIndex, toIndex);
+       }
+
+       @Override
+       public Object[] toArray() {
+               return list.toArray();
+       }
+
+       @Override
+       @SuppressWarnings("unchecked")
+       public <T> T[] toArray(T[] a) {
+               return list.toArray(a);
+       }
+
+       /**
+        * Adds an element to this list with automatic type conversion.
+        *
+        * <p>
+        * This method converts the element using the configured element 
function (if any) and validates
+        * the type (if element type was specified when creating the list). 
After conversion and validation,
+        * the element is added using the standard {@link #add(Object)} method, 
which applies the filter.
+        *
+        * <h5 class='section'>Type Conversion:</h5>
+        * <ul>
+        *      <li>If an element function is configured, the element is 
converted by applying the function
+        *      <li>If no function is configured, the object is used as-is (but 
may be validated if types were specified)
+        * </ul>
+        *
+        * <h5 class='section'>Type Validation:</h5>
+        * <ul>
+        *      <li>If element type was specified (via {@link #create(Class)}), 
the converted element must be an instance of that type
+        *      <li>If {@link #create()} was used (no types specified), 
<c>Object.class</c> is used, which accepts any type
+        * </ul>
+        *
+        * <h5 class='section'>Return Value:</h5>
+        * <ul>
+        *      <li>If the element is added successfully, returns <jk>true</jk>
+        *      <li>If the element is filtered out, returns <jk>false</jk>
+        * </ul>
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .elementFunction(o -&gt; Integer.parseInt(o.toString()))
+        *              .build();
+        *
+        *      <jv>list</jv>.addConverted(<js>"123"</js>);  <jc>// Element 
converted from String to Integer</jc>
+        * </p>
+        *
+        * @param element The element to add. Will be converted if an element 
function is configured.
+        * @return <jk>true</jk> if the element was added, <jk>false</jk> if it 
was filtered out.
+        * @throws RuntimeException If conversion fails or type validation 
fails.
+        */
+       public boolean addConverted(Object element) {
+               E convertedElement = convertElement(element);
+               return add(convertedElement);
+       }
+
+       /**
+        * Adds all elements from the specified collection with automatic type 
conversion.
+        *
+        * <p>
+        * This method iterates over all elements in the source collection and 
adds each one using {@link #addConverted(Object)},
+        * which applies conversion and validation before adding.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .elementFunction(o -&gt; Integer.parseInt(o.toString()))
+        *              .build();
+        *
+        *      Collection&lt;String&gt; <jv>source</jv> = 
List.of(<js>"5"</js>, <js>"-1"</js>);
+        *      <jv>list</jv>.addAllConverted(<jv>source</jv>);  <jc>// 
Elements converted, negative value filtered out</jc>
+        * </p>
+        *
+        * @param source The collection containing elements to add. Can be 
<jk>null</jk> (no-op).
+        * @return This object for method chaining.
+        */
+       public FilteredList<E> addAllConverted(Collection<?> source) {
+               if (source != null) {
+                       for (var e : source) {
+                               addConverted(e);
+                       }
+               }
+               return this;
+       }
+
+       /**
+        * Adds arbitrary values to this list.
+        *
+        * <p>
+        * Objects can be any of the following:
+        * <ul>
+        *      <li>The same type or convertible to the element type of this 
list.
+        *      <li>Collections or arrays of anything on this list.
+        * </ul>
+        *
+        * <p>
+        * Each element from the collections/arrays will be added using {@link 
#add(Object)}, which applies
+        * conversion and filtering.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredList&lt;Integer&gt; <jv>list</jv> = FilteredList
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .build();
+        *
+        *      List&lt;Integer&gt; <jv>list1</jv> = List.of(5, -1);
+        *      List&lt;Integer&gt; <jv>list2</jv> = List.of(10);
+        *      <jv>list</jv>.addAny(<jv>list1</jv>, <jv>list2</jv>);  <jc>// 
Adds 5, 10 (-1 filtered out)</jc>
+        * </p>
+        *
+        * @param values The values to add. Can be <jk>null</jk> or contain 
<jk>null</jk> values (ignored).
+        * @return This object for method chaining.
+        */
+       public FilteredList<E> addAny(Object...values) {
+               if (values != null) {
+                       for (var o : values) {
+                               if (o == null)
+                                       continue;
+                               if (o instanceof Collection<?> c) {
+                                       addAllConverted(c);
+                               } else if (o.getClass().isArray()) {
+                                       for (int i = 0; i < 
java.lang.reflect.Array.getLength(o); i++) {
+                                               
addConverted(java.lang.reflect.Array.get(o, i));
+                                       }
+                               } else {
+                                       addConverted(o);
+                               }
+                       }
+               }
+               return this;
+       }
+
+       private E convertElement(Object element) {
+               if (elementFunction != null) {
+                       element = elementFunction.apply(element);
+               }
+               if (element == null) {
+                       // Allow null for non-primitive types
+                       if (elementType.isPrimitive())
+                               throw rex("Cannot set null element for 
primitive type {0}", elementType.getName());
+                       return null;
+               }
+               if (elementType.isInstance(element))
+                       return elementType.cast(element);
+               throw rex("Object of type {0} could not be converted to element 
type {1}", cn(element), cn(elementType));
+       }
+
+       @Override
+       public String toString() {
+               return list.toString();
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               return list.equals(o);
+       }
+
+       @Override
+       public int hashCode() {
+               return list.hashCode();
+       }
+}
+
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/FilteredSet.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/FilteredSet.java
new file mode 100644
index 0000000000..b57db45912
--- /dev/null
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/FilteredSet.java
@@ -0,0 +1,595 @@
+/*
+ * 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.juneau.commons.collections;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+import static org.apache.juneau.commons.utils.ThrowableUtils.*;
+import static org.apache.juneau.commons.utils.Utils.*;
+
+import java.util.*;
+import java.util.function.*;
+
+/**
+ * A set wrapper that filters elements based on a {@link Predicate} when they 
are added.
+ *
+ * <p>
+ * This class wraps an underlying set and applies a filter to determine 
whether elements should be added.
+ * Only elements that pass the filter (i.e., the predicate returns 
<jk>true</jk>) are actually stored in
+ * the underlying set.
+ *
+ * <h5 class='section'>Features:</h5>
+ * <ul class='spaced-list'>
+ *     <li><b>Flexible Filtering:</b> Use any {@link Predicate} to filter 
elements based on any criteria
+ *     <li><b>Optional Filter:</b> Filter is optional when using the builder - 
defaults to accepting all elements
+ *     <li><b>Multiple Filters:</b> Can combine multiple filters using AND 
logic
+ *     <li><b>Custom Set Types:</b> Works with any set implementation via the 
builder's <c>inner</c> method
+ *     <li><b>Type Conversion:</b> Supports automatic type conversion via 
element function
+ *     <li><b>Convenience Methods:</b> Provides {@link #add(Object)}, {@link 
#addAll(Collection)}, and {@link #addAny(Object...)} for easy element addition
+ *     <li><b>Transparent Interface:</b> Implements the full {@link Set} 
interface, so it can be used anywhere a set is expected
+ *     <li><b>Filter on Add:</b> Filtering happens when elements are added via 
{@link #add(Object)}, {@link #addAll(Collection)}, etc.
+ *     <li><b>Automatic Deduplication:</b> Duplicate elements are 
automatically handled by the underlying set
+ * </ul>
+ *
+ * <h5 class='section'>Usage:</h5>
+ * <p class='bjava'>
+ *     <jc>// Create a filtered set that only accepts non-null values</jc>
+ *     FilteredSet&lt;String&gt; <jv>set</jv> = FilteredSet
+ *             .<jsm>create</jsm>(String.<jk>class</jk>)
+ *             .filter(v -&gt; v != <jk>null</jk>)
+ *             .build();
+ *
+ *     <jv>set</jv>.add(<js>"value1"</js>);  <jc>// Added</jc>
+ *     <jv>set</jv>.add(<jk>null</jk>);      <jc>// Filtered out</jc>
+ *     <jv>set</jv>.add(<js>"value3"</js>);  <jc>// Added</jc>
+ *
+ *     <jc>// set now contains: ["value1", "value3"]</jc>
+ * </p>
+ *
+ * <h5 class='section'>Example - Custom Set Type:</h5>
+ * <p class='bjava'>
+ *     <jc>// Create a filtered TreeSet that only accepts positive numbers</jc>
+ *     FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+ *             .<jsm>create</jsm>(Integer.<jk>class</jk>)
+ *             .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+ *             .inner(<jk>new</jk> TreeSet&lt;&gt;())
+ *             .build();
+ *
+ *     <jv>set</jv>.add(5);   <jc>// Added</jc>
+ *     <jv>set</jv>.add(-1);  <jc>// Filtered out</jc>
+ *     <jv>set</jv>.add(10);  <jc>// Added</jc>
+ * </p>
+ *
+ * <h5 class='section'>Behavior Notes:</h5>
+ * <ul class='spaced-list'>
+ *     <li>Filtering is applied to all element addition methods: {@link 
#add(Object)}, {@link #addAll(Collection)}, etc.
+ *     <li>If the filter returns <jk>false</jk>, the element is silently 
ignored (not added)
+ *     <li>When {@link #add(Object)} filters out an element, it returns 
<jk>false</jk> (element was not added)
+ *     <li>The filter is not applied when reading from the set (e.g., {@link 
#contains(Object)})
+ *     <li>All other set operations behave as expected on the underlying set
+ *     <li>The underlying set type is determined by the <c>inner</c> method 
(defaults to {@link LinkedHashSet})
+ *     <li>Duplicate elements are automatically handled by the underlying set 
(only one occurrence is stored)
+ * </ul>
+ *
+ * <h5 class='section'>Thread Safety:</h5>
+ * <p>
+ * This class is not thread-safe unless the underlying set is thread-safe. If 
thread safety is required,
+ * use a thread-safe set type (e.g., {@link 
java.util.concurrent.CopyOnWriteArraySet}) via the <c>inner</c> method.
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ *     <li class='link'><a class="doclink" 
href="../../../../../index.html#juneau-commons">Overview &gt; juneau-commons</a>
+ * </ul>
+ *
+ * @param <E> The element type.
+ */
+public class FilteredSet<E> extends AbstractSet<E> {
+
+       /**
+        * Builder for creating {@link FilteredSet} instances.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .inner(<jk>new</jk> TreeSet&lt;&gt;())
+        *              .build();
+        * </p>
+        *
+        * @param <E> The element type.
+        */
+       public static class Builder<E> {
+               private Predicate<E> filter = v -> true;
+               private Set<E> inner;
+               private Class<E> elementType;
+               private Function<Object,E> elementFunction;
+
+               /**
+                * Sets the filter predicate that determines whether elements 
should be added.
+                *
+                * <p>
+                * The predicate receives the element value. If it returns 
<jk>true</jk>,
+                * the element is added to the set. If it returns 
<jk>false</jk>, the element is silently ignored.
+                *
+                * <p>
+                * This method is optional. If not called, the set will accept 
all elements (defaults to <c>v -&gt; true</c>).
+                *
+                * <p>
+                * This method can be called multiple times. When called 
multiple times, all filters are combined
+                * using AND logic - an element must pass all filters to be 
added to the set.
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      <jc>// Filter out null values</jc>
+                *      Builder&lt;String&gt; <jv>b</jv> = 
FilteredSet.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk>);
+                *
+                *      <jc>// Filter based on value</jc>
+                *      Builder&lt;Integer&gt; <jv>b2</jv> = 
FilteredSet.<jsm>create</jsm>(Integer.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v 
&gt; 0);
+                *
+                *      <jc>// Multiple filters combined with AND</jc>
+                *      Builder&lt;Integer&gt; <jv>b3</jv> = 
FilteredSet.<jsm>create</jsm>(Integer.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk>)           
<jc>// First filter</jc>
+                *              .filter(v -&gt; v &gt; 0)                    
<jc>// Second filter (ANDed with first)</jc>
+                *              .filter(v -&gt; v &lt; 100);                  
<jc>// Third filter (ANDed with previous)</jc>
+                * </p>
+                *
+                * @param value The filter predicate. Must not be <jk>null</jk>.
+                * @return This object for method chaining.
+                */
+               public Builder<E> filter(Predicate<E> value) {
+                       Predicate<E> newFilter = assertArgNotNull("value", 
value);
+                       if (filter == null)
+                               filter = newFilter;
+                       else
+                               filter = filter.and(newFilter);
+                       return this;
+               }
+
+               /**
+                * Sets the underlying set instance that will store the 
filtered elements.
+                *
+                * <p>
+                * If not specified, a new {@link LinkedHashSet} will be 
created during {@link #build()}.
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      <jc>// Use a TreeSet for sorted order</jc>
+                *      Builder&lt;String&gt; <jv>b</jv> = 
FilteredSet.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .inner(<jk>new</jk> TreeSet&lt;&gt;());
+                *
+                *      <jc>// Use a CopyOnWriteArraySet for thread safety</jc>
+                *      Builder&lt;String&gt; <jv>b2</jv> = 
FilteredSet.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .inner(<jk>new</jk> 
CopyOnWriteArraySet&lt;&gt;());
+                *
+                *      <jc>// Use an existing set</jc>
+                *      Set&lt;String&gt; <jv>existing</jv> = <jk>new</jk> 
LinkedHashSet&lt;&gt;();
+                *      Builder&lt;String&gt; <jv>b3</jv> = 
FilteredSet.<jsm>create</jsm>(String.<jk>class</jk>)
+                *              .inner(<jv>existing</jv>);
+                * </p>
+                *
+                * @param value The underlying set instance. Must not be 
<jk>null</jk>.
+                * @return This object for method chaining.
+                */
+               public Builder<E> inner(Set<E> value) {
+                       inner = assertArgNotNull("value", value);
+                       return this;
+               }
+
+               /**
+                * Sets the function to use for converting elements when using 
{@link FilteredSet#add(Object)}.
+                *
+                * <p>
+                * If specified, elements passed to {@link 
FilteredSet#add(Object)} will be converted using
+                * this function before being added to the set. If not 
specified, elements must already be of the
+                * correct type (or <c>Object.class</c> is used if types were 
not specified, which accepts any type).
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+                *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+                *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v 
&gt; 0)
+                *              .elementFunction(o -&gt; 
Integer.parseInt(o.toString()))
+                *              .build();
+                *
+                *      <jv>set</jv>.add(<js>"123"</js>);  <jc>// Element will 
be converted from String to Integer</jc>
+                * </p>
+                *
+                * @param value The element conversion function. Can be 
<jk>null</jk>.
+                * @return This object for method chaining.
+                */
+               public Builder<E> elementFunction(Function<Object,E> value) {
+                       elementFunction = value;
+                       return this;
+               }
+
+               /**
+                * Builds a new {@link FilteredSet} instance.
+                *
+                * <p>
+                * If {@link #filter(Predicate)} was not called, the set will 
accept all elements
+                * (defaults to <c>v -&gt; true</c>).
+                *
+                * @return A new filtered set instance.
+                */
+               public FilteredSet<E> build() {
+                       Set<E> set = inner != null ? inner : new 
LinkedHashSet<>();
+                       return new FilteredSet<>(filter, set, elementType, 
elementFunction);
+               }
+       }
+
+       /**
+        * Creates a new builder for constructing a filtered set.
+        *
+        * @param <E> The element type.
+        * @param elementType The element type class. Must not be <jk>null</jk>.
+        * @return A new builder.
+        */
+       public static <E> Builder<E> create(Class<E> elementType) {
+               assertArgNotNull("elementType", elementType);
+               var builder = new Builder<E>();
+               builder.elementType = elementType;
+               return builder;
+       }
+
+       /**
+        * Creates a new builder for constructing a filtered set with generic 
types.
+        *
+        * <p>
+        * This is a convenience method that creates a builder without 
requiring explicit type class parameters.
+        * The generic types must be explicitly specified using the diamond 
operator syntax.
+        *
+        * <p>
+        * When using this method, type checking is effectively disabled (using 
<c>Object.class</c> as the type),
+        * allowing any element types to be added. However, the generic type 
parameter should still be
+        * specified for type safety.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Explicitly specify generic types using diamond 
operator</jc>
+        *      <jk>var</jk> <jv>set</jv> = FilteredSet
+        *              .&lt;String&gt;<jsm>create</jsm>()
+        *              .filter(<jv>v</jv> -&gt; <jv>v</jv> != <jk>null</jk>)
+        *              .build();
+        * </p>
+        *
+        * @param <E> The element type.
+        * @return A new builder.
+        */
+       @SuppressWarnings("unchecked")
+       public static <E> Builder<E> create() {
+               var builder = new Builder<E>();
+               builder.elementType = (Class<E>)Object.class;
+               return builder;
+       }
+       private final Predicate<E> filter;
+       private final Set<E> set;
+       private final Class<E> elementType;
+       private final Function<Object,E> elementFunction;
+
+       /**
+        * Constructor.
+        *
+        * @param filter The filter predicate. Can be <jk>null</jk> (if null, 
all elements are accepted).
+        * @param set The underlying set. Must not be <jk>null</jk>.
+        * @param elementType The element type. Must not be <jk>null</jk> (use 
<c>Object.class</c> to disable type checking).
+        * @param elementFunction The element conversion function, or 
<jk>null</jk> if not specified.
+        */
+       protected FilteredSet(Predicate<E> filter, Set<E> set, Class<E> 
elementType, Function<Object,E> elementFunction) {
+               this.filter = assertArgNotNull("filter", filter);
+               this.set = assertArgNotNull("set", set);
+               this.elementType = assertArgNotNull("elementType", elementType);
+               this.elementFunction = elementFunction;
+       }
+
+       /**
+        * Adds the specified element to this set, if it passes the filter.
+        *
+        * <p>
+        * If the element passes the filter, it is added to the set and this 
method returns <jk>true</jk>
+        * (or <jk>false</jk> if the element was already present in the set).
+        *
+        * <p>
+        * If the element is filtered out, it is not added to the set and this 
method returns <jk>false</jk>.
+        *
+        * @param e The element to be added to this set.
+        * @return <jk>true</jk> if the element was added, <jk>false</jk> if it 
was filtered out or already present.
+        */
+       @Override
+       public boolean add(E e) {
+               if (filter.test(e))
+                       return set.add(e);
+               return false;
+       }
+
+       @Override
+       public boolean addAll(Collection<? extends E> c) {
+               boolean modified = false;
+               for (var e : c) {
+                       if (filter.test(e)) {
+                               if (set.add(e))
+                                       modified = true;
+                       }
+               }
+               return modified;
+       }
+
+       /**
+        * Tests whether the specified element would pass the filter and be 
added to this set.
+        *
+        * <p>
+        * This method can be used to check if an element would be accepted by 
the filter without actually
+        * adding it to the set. This is useful for validation, debugging, or 
pre-checking elements before
+        * attempting to add them.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .build();
+        *
+        *      <jk>if</jk> (<jv>set</jv>.wouldAccept(5)) {
+        *              <jv>set</jv>.add(5);  <jc>// Will be added</jc>
+        *      }
+        * </p>
+        *
+        * @param element The element to test.
+        * @return <jk>true</jk> if the element would be added to the set, 
<jk>false</jk> if it would be filtered out.
+        */
+       public boolean wouldAccept(E element) {
+               return filter.test(element);
+       }
+
+       /**
+        * Returns the filter predicate used by this set.
+        *
+        * <p>
+        * This method provides access to the filter for debugging, inspection, 
or advanced use cases.
+        * The returned predicate is the combined filter used internally by 
this set. If multiple filters
+        * were set via the builder, they are combined using AND logic.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .build();
+        *
+        *      Predicate&lt;Integer&gt; <jv>filter</jv> = 
<jv>set</jv>.getFilter();
+        *      <jc>// Use filter for other purposes</jc>
+        * </p>
+        *
+        * @return The filter predicate. Never <jk>null</jk>.
+        */
+       public Predicate<E> getFilter() {
+               return filter;
+       }
+
+       @Override
+       public Iterator<E> iterator() {
+               return set.iterator();
+       }
+
+       @Override
+       public int size() {
+               return set.size();
+       }
+
+       @Override
+       public boolean isEmpty() {
+               return set.isEmpty();
+       }
+
+       @Override
+       public boolean contains(Object o) {
+               return set.contains(o);
+       }
+
+       @Override
+       public boolean containsAll(Collection<?> c) {
+               return set.containsAll(c);
+       }
+
+       @Override
+       public boolean remove(Object o) {
+               return set.remove(o);
+       }
+
+       @Override
+       public boolean removeAll(Collection<?> c) {
+               return set.removeAll(c);
+       }
+
+       @Override
+       public boolean retainAll(Collection<?> c) {
+               return set.retainAll(c);
+       }
+
+       @Override
+       public void clear() {
+               set.clear();
+       }
+
+       @Override
+       public Object[] toArray() {
+               return set.toArray();
+       }
+
+       @Override
+       @SuppressWarnings("unchecked")
+       public <T> T[] toArray(T[] a) {
+               return set.toArray(a);
+       }
+
+       /**
+        * Adds an element to this set with automatic type conversion.
+        *
+        * <p>
+        * This method converts the element using the configured element 
function (if any) and validates
+        * the type (if element type was specified when creating the set). 
After conversion and validation,
+        * the element is added using the standard {@link #add(Object)} method, 
which applies the filter.
+        *
+        * <h5 class='section'>Type Conversion:</h5>
+        * <ul>
+        *      <li>If an element function is configured, the element is 
converted by applying the function
+        *      <li>If no function is configured, the object is used as-is (but 
may be validated if types were specified)
+        * </ul>
+        *
+        * <h5 class='section'>Type Validation:</h5>
+        * <ul>
+        *      <li>If element type was specified (via {@link #create(Class)}), 
the converted element must be an instance of that type
+        *      <li>If {@link #create()} was used (no types specified), 
<c>Object.class</c> is used, which accepts any type
+        * </ul>
+        *
+        * <h5 class='section'>Return Value:</h5>
+        * <ul>
+        *      <li>If the element is added successfully, returns <jk>true</jk>
+        *      <li>If the element is filtered out or already present, returns 
<jk>false</jk>
+        * </ul>
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .elementFunction(o -&gt; Integer.parseInt(o.toString()))
+        *              .build();
+        *
+        *      <jv>set</jv>.addConverted(<js>"123"</js>);  <jc>// Element 
converted from String to Integer</jc>
+        * </p>
+        *
+        * @param element The element to add. Will be converted if an element 
function is configured.
+        * @return <jk>true</jk> if the element was added, <jk>false</jk> if it 
was filtered out or already present.
+        * @throws RuntimeException If conversion fails or type validation 
fails.
+        */
+       public boolean addConverted(Object element) {
+               E convertedElement = convertElement(element);
+               return add(convertedElement);
+       }
+
+       /**
+        * Adds all elements from the specified collection with automatic type 
conversion.
+        *
+        * <p>
+        * This method iterates over all elements in the source collection and 
adds each one using {@link #addConverted(Object)},
+        * which applies conversion and validation before adding.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .elementFunction(o -&gt; Integer.parseInt(o.toString()))
+        *              .build();
+        *
+        *      Collection&lt;String&gt; <jv>source</jv> = 
List.of(<js>"5"</js>, <js>"-1"</js>);
+        *      <jv>set</jv>.addAllConverted(<jv>source</jv>);  <jc>// Elements 
converted, negative value filtered out</jc>
+        * </p>
+        *
+        * @param source The collection containing elements to add. Can be 
<jk>null</jk> (no-op).
+        * @return This object for method chaining.
+        */
+       public FilteredSet<E> addAllConverted(Collection<?> source) {
+               if (source != null) {
+                       for (var e : source) {
+                               addConverted(e);
+                       }
+               }
+               return this;
+       }
+
+       /**
+        * Adds arbitrary values to this set.
+        *
+        * <p>
+        * Objects can be any of the following:
+        * <ul>
+        *      <li>The same type or convertible to the element type of this 
set.
+        *      <li>Collections or arrays of anything on this set.
+        * </ul>
+        *
+        * <p>
+        * Each element from the collections/arrays will be added using {@link 
#add(Object)}, which applies
+        * conversion and filtering.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      FilteredSet&lt;Integer&gt; <jv>set</jv> = FilteredSet
+        *              .<jsm>create</jsm>(Integer.<jk>class</jk>)
+        *              .filter(v -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
+        *              .build();
+        *
+        *      Set&lt;Integer&gt; <jv>set1</jv> = Set.of(5, -1);
+        *      Set&lt;Integer&gt; <jv>set2</jv> = Set.of(10);
+        *      <jv>set</jv>.addAny(<jv>set1</jv>, <jv>set2</jv>);  <jc>// Adds 
5, 10 (-1 filtered out)</jc>
+        * </p>
+        *
+        * @param values The values to add. Can be <jk>null</jk> or contain 
<jk>null</jk> values (ignored).
+        * @return This object for method chaining.
+        */
+       public FilteredSet<E> addAny(Object...values) {
+               if (values != null) {
+                       for (var o : values) {
+                               if (o == null)
+                                       continue;
+                               if (o instanceof Collection<?> c) {
+                                       addAllConverted(c);
+                               } else if (o.getClass().isArray()) {
+                                       for (int i = 0; i < 
java.lang.reflect.Array.getLength(o); i++) {
+                                               
addConverted(java.lang.reflect.Array.get(o, i));
+                                       }
+                               } else {
+                                       addConverted(o);
+                               }
+                       }
+               }
+               return this;
+       }
+
+       private E convertElement(Object element) {
+               if (elementFunction != null) {
+                       element = elementFunction.apply(element);
+               }
+               if (element == null) {
+                       // Allow null for non-primitive types
+                       if (elementType.isPrimitive())
+                               throw rex("Cannot set null element for 
primitive type {0}", elementType.getName());
+                       return null;
+               }
+               if (elementType.isInstance(element))
+                       return elementType.cast(element);
+               throw rex("Object of type {0} could not be converted to element 
type {1}", cn(element), cn(elementType));
+       }
+
+       @Override
+       public String toString() {
+               return set.toString();
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               return set.equals(o);
+       }
+
+       @Override
+       public int hashCode() {
+               return set.hashCode();
+       }
+}
+
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/ListBuilder.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/ListBuilder.java
index b1f53f60ae..0de6e3f0ea 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/ListBuilder.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/ListBuilder.java
@@ -119,7 +119,7 @@ public class ListBuilder<E> {
        }
 
        private List<E> list;
-       private boolean unmodifiable = false, sparse = false;
+       private boolean unmodifiable = false, sparse = false, concurrent = 
false;
        private Comparator<E> comparator;
        private List<Converter> converters;
 
@@ -277,6 +277,8 @@ public class ListBuilder<E> {
                if (nn(list)) {
                        if (nn(comparator))
                                Collections.sort(list, comparator);
+                       if (concurrent)
+                               list = synchronizedList(list);
                        if (unmodifiable)
                                list = unmodifiableList(list);
                }
@@ -377,4 +379,51 @@ public class ListBuilder<E> {
                this.unmodifiable = true;
                return this;
        }
+
+       /**
+        * When specified, {@link #build()} will return a thread-safe 
synchronized list.
+        *
+        * <p>
+        * The list will be wrapped using {@link 
Collections#synchronizedList(List)} to provide thread-safety.
+        * This is useful when the list needs to be accessed from multiple 
threads.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Create a thread-safe list</jc>
+        *      List&lt;String&gt; <jv>list</jv> = 
ListBuilder.<jsm>create</jsm>(String.<jk>class</jk>)
+        *              .add(<js>"one"</js>, <js>"two"</js>)
+        *              .concurrent()
+        *              .build();
+        * </p>
+        *
+        * @return This object.
+        */
+       public ListBuilder<E> concurrent() {
+               concurrent = true;
+               return this;
+       }
+
+       /**
+        * Sets whether {@link #build()} should return a thread-safe 
synchronized list.
+        *
+        * <p>
+        * When <c>true</c>, the list will be wrapped using {@link 
Collections#synchronizedList(List)} to provide thread-safety.
+        * This is useful when the list needs to be accessed from multiple 
threads.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Conditionally create a thread-safe list</jc>
+        *      List&lt;String&gt; <jv>list</jv> = 
ListBuilder.<jsm>create</jsm>(String.<jk>class</jk>)
+        *              .add(<js>"one"</js>, <js>"two"</js>)
+        *              .concurrent(<jv>needsThreadSafety</jv>)
+        *              .build();
+        * </p>
+        *
+        * @param value Whether to make the list thread-safe.
+        * @return This object.
+        */
+       public ListBuilder<E> concurrent(boolean value) {
+               concurrent = value;
+               return this;
+       }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/MapBuilder.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/MapBuilder.java
index f619869d11..6073bda4e9 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/MapBuilder.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/MapBuilder.java
@@ -22,6 +22,7 @@ import static org.apache.juneau.commons.utils.Utils.*;
 
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.concurrent.*;
 import java.util.function.*;
 
 /**
@@ -160,7 +161,7 @@ public class MapBuilder<K,V> {
        }
 
        private Map<K,V> map;
-       private boolean unmodifiable = false, sparse = false;
+       private boolean unmodifiable = false, sparse = false, concurrent = 
false, ordered = false;
        private Comparator<K> comparator;
 
        private BiPredicate<K,V> filter;
@@ -274,53 +275,56 @@ public class MapBuilder<K,V> {
         * Builds the map.
         *
         * <p>
-        * Applies filtering, sorting, unmodifiable, and sparse options.
+        * Applies filtering, sorting, ordering, concurrent, unmodifiable, and 
sparse options.
         *
         * <p>
-        * If filtering is applied, the result is wrapped in a {@link 
FilteredMap}. If sorting is applied,
-        * a {@link TreeMap} is used as the underlying map; otherwise a {@link 
LinkedHashMap} is used.
+        * Map type selection:
+        * <ul>
+        *      <li>If {@link #sorted()} is set: Uses {@link TreeMap} (or 
{@link java.util.concurrent.ConcurrentSkipListMap} if concurrent)
+        *      <li>If {@link #ordered()} is set: Uses {@link LinkedHashMap} 
(or synchronized LinkedHashMap if concurrent)
+        *      <li>Otherwise: Uses {@link HashMap} (or {@link 
java.util.concurrent.ConcurrentHashMap} if concurrent)
+        * </ul>
+        *
+        * <p>
+        * If filtering is applied, the result is wrapped in a {@link 
FilteredMap}.
         *
         * @return The built map, or {@code null} if {@link #sparse()} is set 
and the map is empty.
         */
        public Map<K,V> build() {
-               if (sparse) {
-                       if (nn(map) && map.isEmpty())
-                               return null;
+
+               if (sparse && isEmpty(map))
+                       return null;
+
+               var map2 = (Map<K,V>)null;
+
+               if (ordered) {
+                       map2 = new LinkedHashMap<>();
+                       if (concurrent)
+                               map2 = Collections.synchronizedMap(map2);
+               } else if (nn(comparator)) {
+                       map2 = concurrent ? new 
ConcurrentSkipListMap<>(comparator) : new TreeMap<>(comparator);
                } else {
-                       if (map == null)
-                               map = new LinkedHashMap<>();
+                       map2 = concurrent ? new ConcurrentHashMap<>() : new 
HashMap<>();
                }
 
-               Map<K,V> result = map;
-
-               if (nn(result)) {
-                       // Apply filtering if specified
-                       if (nn(filter)) {
-                               Map<K,V> innerMap = nn(comparator)
-                                       ? new TreeMap<>(comparator)
-                                       : new LinkedHashMap<>();
-
-                               var filteredMap = 
FilteredMap.<K,V>create(keyType, valueType)
-                                       .filter(filter)
-                                       .inner(innerMap)
-                                       .build();
-
-                               // Add all entries to the filtered map
-                               result.forEach(filteredMap::put);
-                               result = filteredMap;
-                       } else if (nn(comparator)) {
-                               // Apply sorting if no filter
-                               var m2 = new TreeMap<K,V>(comparator);
-                               m2.putAll(result);
-                               result = m2;
-                       }
-
-                       // Apply unmodifiable if specified
-                       if (unmodifiable)
-                               result = Collections.unmodifiableMap(result);
+               if (nn(filter) || nn(keyFunction) || nn(valueFunction)) {
+                       var map3b = FilteredMap.create(keyType, valueType);
+                       if (nn(filter))
+                               map3b.filter(filter);
+                       if (nn(keyFunction))
+                               map3b.keyFunction(keyFunction);
+                       if (nn(valueFunction))
+                               map3b.valueFunction(valueFunction);
+                       map2 = map3b.inner(map2).build();
                }
 
-               return result;
+               if (nn(map))
+                       map2.putAll(map);
+
+               if (unmodifiable)
+                       map2 = Collections.unmodifiableMap(map2);
+
+               return map2;
        }
 
        /**
@@ -350,7 +354,12 @@ public class MapBuilder<K,V> {
         * Builds the map as a {@link FilteredMap}.
         *
         * <p>
-        * If sorting is applied, a {@link TreeMap} is used as the underlying 
map; otherwise a {@link LinkedHashMap} is used.
+        * Map type selection:
+        * <ul>
+        *      <li>If {@link #sorted()} is set: Uses {@link TreeMap} (or 
{@link java.util.concurrent.ConcurrentSkipListMap} if concurrent)
+        *      <li>If {@link #ordered()} is set: Uses {@link LinkedHashMap} 
(or synchronized LinkedHashMap if concurrent)
+        *      <li>Otherwise: Uses {@link HashMap} (or {@link 
java.util.concurrent.ConcurrentHashMap} if concurrent)
+        * </ul>
         *
         * <h5 class='section'>Example:</h5>
         * <p class='bjava'>
@@ -530,6 +539,10 @@ public class MapBuilder<K,V> {
        /**
         * Converts the set into a {@link SortedMap}.
         *
+        * <p>
+        * Note: If {@link #ordered()} was previously called, calling this 
method will override it.
+        * The last method called ({@link #ordered()} or {@link #sorted()}) 
determines the final map type.
+        *
         * @return This object.
         */
        @SuppressWarnings("unchecked")
@@ -540,11 +553,16 @@ public class MapBuilder<K,V> {
        /**
         * Converts the set into a {@link SortedMap} using the specified 
comparator.
         *
+        * <p>
+        * Note: If {@link #ordered()} was previously called, calling this 
method will override it.
+        * The last method called ({@link #ordered()} or {@link #sorted()}) 
determines the final map type.
+        *
         * @param comparator The comparator to use for sorting. Must not be 
<jk>null</jk>.
         * @return This object.
         */
        public MapBuilder<K,V> sorted(Comparator<K> comparator) {
                this.comparator = assertArgNotNull("comparator", comparator);
+               ordered = false;
                return this;
        }
 
@@ -565,7 +583,7 @@ public class MapBuilder<K,V> {
         * Specifies the map to append to.
         *
         * <p>
-        * If not specified, uses a new {@link LinkedHashMap}.
+        * If not specified, uses a new {@link HashMap} (or {@link 
LinkedHashMap} if {@link #ordered()} is set).
         *
         * @param map The map to append to.
         * @return This object.
@@ -585,6 +603,139 @@ public class MapBuilder<K,V> {
                return this;
        }
 
+       /**
+        * When specified, {@link #build()} will return a thread-safe map.
+        *
+        * <p>
+        * The thread-safety implementation depends on other settings:
+        * <ul>
+        *      <li>If {@link #sorted()} is set: Uses {@link 
java.util.concurrent.ConcurrentSkipListMap}
+        *      <li>If {@link #ordered()} is set: Uses {@link 
Collections#synchronizedMap(LinkedHashMap)}
+        *      <li>Otherwise: Uses {@link 
java.util.concurrent.ConcurrentHashMap}
+        * </ul>
+        *
+        * <p>
+        * This is useful when the map needs to be accessed from multiple 
threads.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jk>import static</jk> 
org.apache.juneau.commons.utils.CollectionUtils.*;
+        *
+        *      <jc>// Create a thread-safe map using ConcurrentHashMap</jc>
+        *      Map&lt;String,Integer&gt; <jv>map</jv> = 
<jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
+        *              .add(<js>"one"</js>, 1)
+        *              .add(<js>"two"</js>, 2)
+        *              .concurrent()
+        *              .build();
+        *
+        *      <jc>// Create a thread-safe ordered map</jc>
+        *      Map&lt;String,Integer&gt; <jv>map2</jv> = 
<jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
+        *              .ordered()
+        *              .concurrent()
+        *              .add(<js>"one"</js>, 1)
+        *              .build();
+        * </p>
+        *
+        * @return This object.
+        */
+       public MapBuilder<K,V> concurrent() {
+               concurrent = true;
+               return this;
+       }
+
+       /**
+        * Sets whether {@link #build()} should return a thread-safe map.
+        *
+        * <p>
+        * The thread-safety implementation depends on other settings:
+        * <ul>
+        *      <li>If {@link #sorted()} is set: Uses {@link 
java.util.concurrent.ConcurrentSkipListMap}
+        *      <li>If {@link #ordered()} is set: Uses {@link 
Collections#synchronizedMap(LinkedHashMap)}
+        *      <li>Otherwise: Uses {@link 
java.util.concurrent.ConcurrentHashMap}
+        * </ul>
+        *
+        * <p>
+        * This is useful when the map needs to be accessed from multiple 
threads.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jk>import static</jk> 
org.apache.juneau.commons.utils.CollectionUtils.*;
+        *
+        *      <jc>// Conditionally create a thread-safe map</jc>
+        *      Map&lt;String,Integer&gt; <jv>map</jv> = 
<jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
+        *              .add(<js>"one"</js>, 1)
+        *              .concurrent(<jv>needsThreadSafety</jv>)
+        *              .build();
+        * </p>
+        *
+        * @param value Whether to make the map thread-safe.
+        * @return This object.
+        */
+       public MapBuilder<K,V> concurrent(boolean value) {
+               concurrent = value;
+               return this;
+       }
+
+       /**
+        * When specified, {@link #build()} will use a {@link LinkedHashMap} to 
preserve insertion order.
+        *
+        * <p>
+        * If not specified, a {@link HashMap} is used by default (no 
guaranteed order).
+        *
+        * <p>
+        * Note: If {@link #sorted()} was previously called, calling this 
method will override it.
+        * The last method called ({@link #ordered()} or {@link #sorted()}) 
determines the final map type.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jk>import static</jk> 
org.apache.juneau.commons.utils.CollectionUtils.*;
+        *
+        *      <jc>// Create an ordered map (preserves insertion order)</jc>
+        *      Map&lt;String,Integer&gt; <jv>map</jv> = 
<jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
+        *              .ordered()
+        *              .add(<js>"one"</js>, 1)
+        *              .add(<js>"two"</js>, 2)
+        *              .build();
+        * </p>
+        *
+        * @return This object.
+        */
+       public MapBuilder<K,V> ordered() {
+               return ordered(true);
+       }
+
+       /**
+        * Sets whether {@link #build()} should use a {@link LinkedHashMap} to 
preserve insertion order.
+        *
+        * <p>
+        * If <c>false</c> (default), a {@link HashMap} is used (no guaranteed 
order).
+        * If <c>true</c>, a {@link LinkedHashMap} is used (preserves insertion 
order).
+        *
+        * <p>
+        * Note: If {@link #sorted()} was previously called, calling this 
method with <c>true</c> will override it.
+        * The last method called ({@link #ordered()} or {@link #sorted()}) 
determines the final map type.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jk>import static</jk> 
org.apache.juneau.commons.utils.CollectionUtils.*;
+        *
+        *      <jc>// Conditionally create an ordered map</jc>
+        *      Map&lt;String,Integer&gt; <jv>map</jv> = 
<jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
+        *              .ordered(<jv>preserveOrder</jv>)
+        *              .add(<js>"one"</js>, 1)
+        *              .build();
+        * </p>
+        *
+        * @param value Whether to preserve insertion order.
+        * @return This object.
+        */
+       public MapBuilder<K,V> ordered(boolean value) {
+               ordered = value;
+               if (ordered)
+                       comparator = null;
+               return this;
+       }
+
        /**
         * Converts a key object to the key type.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/SetBuilder.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/SetBuilder.java
index 8529e509c6..1f77101821 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/SetBuilder.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/SetBuilder.java
@@ -120,7 +120,7 @@ public class SetBuilder<E> {
        }
 
        private Set<E> set;
-       private boolean unmodifiable, sparse;
+       private boolean unmodifiable, sparse, concurrent;
 
        private Comparator<E> comparator;
        private Class<E> elementType;
@@ -280,6 +280,8 @@ public class SetBuilder<E> {
                                s.addAll(set);
                                set = s;
                        }
+                       if (concurrent)
+                               set = synchronizedSet(set);
                        if (unmodifiable)
                                set = unmodifiableSet(set);
                }
@@ -380,4 +382,51 @@ public class SetBuilder<E> {
                unmodifiable = true;
                return this;
        }
+
+       /**
+        * When specified, {@link #build()} will return a thread-safe 
synchronized set.
+        *
+        * <p>
+        * The set will be wrapped using {@link 
Collections#synchronizedSet(Set)} to provide thread-safety.
+        * This is useful when the set needs to be accessed from multiple 
threads.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Create a thread-safe set</jc>
+        *      Set&lt;String&gt; <jv>set</jv> = 
SetBuilder.<jsm>create</jsm>(String.<jk>class</jk>)
+        *              .add(<js>"one"</js>, <js>"two"</js>)
+        *              .concurrent()
+        *              .build();
+        * </p>
+        *
+        * @return This object.
+        */
+       public SetBuilder<E> concurrent() {
+               concurrent = true;
+               return this;
+       }
+
+       /**
+        * Sets whether {@link #build()} should return a thread-safe 
synchronized set.
+        *
+        * <p>
+        * When <c>true</c>, the set will be wrapped using {@link 
Collections#synchronizedSet(Set)} to provide thread-safety.
+        * This is useful when the set needs to be accessed from multiple 
threads.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jc>// Conditionally create a thread-safe set</jc>
+        *      Set&lt;String&gt; <jv>set</jv> = 
SetBuilder.<jsm>create</jsm>(String.<jk>class</jk>)
+        *              .add(<js>"one"</js>, <js>"two"</js>)
+        *              .concurrent(<jv>needsThreadSafety</jv>)
+        *              .build();
+        * </p>
+        *
+        * @param value Whether to make the set thread-safe.
+        * @return This object.
+        */
+       public SetBuilder<E> concurrent(boolean value) {
+               concurrent = value;
+               return this;
+       }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
index 5e94677692..18a0660548 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
@@ -1554,7 +1554,7 @@ public class CollectionUtils {
         * @see MapBuilder
         */
        public static MapBuilder<String,Object> mapb() {
-               return MapBuilder.create(String.class, Object.class);
+               return MapBuilder.create(String.class, Object.class).ordered();
        }
 
        /**
@@ -1568,7 +1568,7 @@ public class CollectionUtils {
         * @return A new map builder.
         */
        public static <K,V> MapBuilder<K,V> mapb(Class<K> keyType, Class<V> 
valueType) {
-               return MapBuilder.create(keyType, valueType);
+               return MapBuilder.create(keyType, valueType).ordered();
        }
 
        /**
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
index 0774097927..da7b7afc7a 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
@@ -1001,6 +1001,18 @@ public class Utils {
                return value != null;
        }
 
+       public static <T> boolean isNull(T value) {
+               return value == null;
+       }
+
+       public static <T> boolean isAllNull(T...values) {
+               if (values == null) return true;
+               for (var v : values)
+                       if (v != null)
+                               return false;
+               return true;
+       }
+
        /**
         * Checks if the specified Boolean is not <jk>null</jk> and is 
<jk>true</jk>.
         *
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java
index c52a2f311d..405e18277e 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java
@@ -208,7 +208,7 @@ class Callback_Test extends TestBase {
                        assertMapped(
                                TESTER.bean(), (obj,prop) -> cns(obj.get(prop, 
Object.class)),
                                "callbacks,x1,x2",
-                               "LinkedHashMap,String,<null>"
+                               "FilteredMap,String,<null>"
                        );
                }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
index c22cc82483..7a5a1254e7 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
@@ -214,7 +214,7 @@ class Components_Test extends TestBase {
                        assertMapped(
                                TESTER.bean(), (obj,prop) -> cns(obj.get(prop, 
Object.class)),
                                
"callbacks,examples,headers,links,parameters,requestBodies,responses,schemas,securitySchemes,x1,x2",
-                               
"LinkedHashMap,LinkedHashMap,LinkedHashMap,LinkedHashMap,LinkedHashMap,LinkedHashMap,LinkedHashMap,LinkedHashMap,LinkedHashMap,String,<null>"
+                               
"FilteredMap,FilteredMap,FilteredMap,FilteredMap,FilteredMap,FilteredMap,FilteredMap,FilteredMap,FilteredMap,String,<null>"
                        );
                }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/OpenApi_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/OpenApi_Test.java
index 388f3da2ee..4d2872c059 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/OpenApi_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/OpenApi_Test.java
@@ -281,7 +281,7 @@ class OpenApi_Test extends TestBase {
                        assertMapped(
                                TESTER.bean(), (obj,prop) -> cns(obj.get(prop, 
Object.class)),
                                
"components,externalDocs,info,openapi,paths,security,servers,tags,x1,x2",
-                               
"Components,ExternalDocumentation,Info,String,TreeMap,ArrayList,ArrayList,ArrayList,String,<null>"
+                               
"Components,ExternalDocumentation,Info,String,FilteredMap,ArrayList,ArrayList,ArrayList,String,<null>"
                        );
                }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
index 35a2ae5442..800bbc93ac 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
@@ -336,7 +336,7 @@ class Operation_Test extends TestBase {
                        assertMapped(
                                TESTER.bean(), (obj,prop) -> cns(obj.get(prop, 
Object.class)),
                                
"callbacks,deprecated,description,externalDocs,operationId,parameters,requestBody,responses,security,servers,summary,tags,x1,x2",
-                               
"LinkedHashMap,Boolean,String,ExternalDocumentation,String,ArrayList,RequestBodyInfo,LinkedHashMap,ArrayList,ArrayList,String,ArrayList,String,<null>"
+                               
"FilteredMap,Boolean,String,ExternalDocumentation,String,ArrayList,RequestBodyInfo,FilteredMap,ArrayList,ArrayList,String,ArrayList,String,<null>"
                        );
                }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
index 987aa6c513..6f6b65acf4 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
@@ -243,7 +243,7 @@ class Parameter_Test extends TestBase {
                        assertMapped(
                                TESTER.bean(), (obj,prop) -> cns(obj.get(prop, 
Object.class)),
                                
"allowEmptyValue,allowReserved,content,deprecated,description,example,examples,explode,in,name,required,schema,style,x1,x2",
-                               
"Boolean,Boolean,LinkedHashMap,Boolean,String,String,LinkedHashMap,Boolean,String,String,Boolean,SchemaInfo,String,String,<null>"
+                               
"Boolean,Boolean,LinkedHashMap,Boolean,String,String,FilteredMap,Boolean,String,String,Boolean,SchemaInfo,String,String,<null>"
                        );
                }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/FilteredList_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/FilteredList_Test.java
new file mode 100644
index 0000000000..2235972aac
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/FilteredList_Test.java
@@ -0,0 +1,968 @@
+/*
+ * 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.juneau.commons.collections;
+
+import static org.apache.juneau.TestUtils.assertThrowsWithMessage;
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.*;
+
+import org.apache.juneau.*;
+import org.junit.jupiter.api.*;
+
+class FilteredList_Test extends TestBase {
+
+       
//====================================================================================================
+       // Basic filtering - filter out null values
+       
//====================================================================================================
+
+       @Test
+       void a01_filterNullValues_add() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(list.add("value1"));  // Added
+               assertFalse(list.add(null));      // Filtered out
+               assertTrue(list.add("value3"));  // Added
+
+               assertSize(2, list);
+               assertEquals("value1", list.get(0));
+               assertEquals("value3", list.get(1));
+       }
+
+       @Test
+       void a02_filterNullValues_addAll() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               var source = list("value1", null, "value3", null);
+
+               list.addAll(source);
+
+               assertSize(2, list);
+               assertEquals("value1", list.get(0));
+               assertEquals("value3", list.get(1));
+       }
+
+       @Test
+       void a03_filterNullValues_addAtIndex() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+               list.add(1, "value3");  // Added at index 1
+               list.add(1, null);      // Filtered out, not added
+
+               assertSize(3, list);
+               assertEquals("value1", list.get(0));
+               assertEquals("value3", list.get(1));
+               assertEquals("value2", list.get(2));
+       }
+
+       
//====================================================================================================
+       // Filter based on value - positive numbers only
+       
//====================================================================================================
+
+       @Test
+       void b01_filterPositiveNumbers() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertTrue(list.add(5));   // Added
+               assertFalse(list.add(-1));  // Filtered out
+               assertFalse(list.add(0));   // Filtered out
+               assertTrue(list.add(10));  // Added
+               assertFalse(list.add(null)); // Filtered out
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+       }
+
+       
//====================================================================================================
+       // Filter based on string length
+       
//====================================================================================================
+
+       @Test
+       void c01_filterStringLength() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null && v.length() > 3)
+                       .build();
+
+               assertTrue(list.add("short"));   // Added
+               assertFalse(list.add("ab"));      // Filtered out (length <= 3)
+               assertTrue(list.add("longer"));  // Added
+               assertFalse(list.add(null));      // Filtered out
+
+               assertSize(2, list);
+               assertEquals("short", list.get(0));
+               assertEquals("longer", list.get(1));
+       }
+
+       
//====================================================================================================
+       // Custom list types - LinkedList
+       
//====================================================================================================
+
+       @Test
+       void d01_customListType_LinkedList() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .inner(new LinkedList<>())
+                       .build();
+
+               list.add(5);
+               list.add(-1);  // Filtered out
+               list.add(10);
+
+               assertSize(2, list);
+               assertTrue(list instanceof FilteredList);
+       }
+
+       
//====================================================================================================
+       // Custom list types - CopyOnWriteArrayList
+       
//====================================================================================================
+
+       @Test
+       void d02_customListType_CopyOnWriteArrayList() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .inner(new CopyOnWriteArrayList<>())
+                       .build();
+
+               list.add("value1");
+               list.add(null);  // Filtered out
+               list.add("value3");
+
+               assertSize(2, list);
+       }
+
+       
//====================================================================================================
+       // List interface methods - get, contains, indexOf
+       
//====================================================================================================
+
+       @Test
+       void e01_listInterface_get() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add(null);  // Filtered out
+
+               assertEquals("value1", list.get(0));
+               assertThrows(IndexOutOfBoundsException.class, () -> 
list.get(1));
+       }
+
+       @Test
+       void e02_listInterface_contains() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add(null);  // Filtered out
+
+               assertTrue(list.contains("value1"));
+               assertFalse(list.contains(null));  // null was filtered out
+       }
+
+       @Test
+       void e03_listInterface_indexOf() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+               list.add(null);  // Filtered out
+               list.add("value1");
+
+               assertEquals(0, list.indexOf("value1"));
+               assertEquals(1, list.indexOf("value2"));
+               assertEquals(-1, list.indexOf(null));  // null was filtered out
+               assertEquals(2, list.lastIndexOf("value1"));
+       }
+
+       @Test
+       void e04_listInterface_size() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertEquals(0, list.size());
+               list.add("value1");
+               assertEquals(1, list.size());
+               list.add(null);  // Filtered out
+               assertEquals(1, list.size());
+               list.add("value3");
+               assertEquals(2, list.size());
+       }
+
+       @Test
+       void e05_listInterface_isEmpty() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(list.isEmpty());
+               list.add("value1");
+               assertFalse(list.isEmpty());
+       }
+
+       @Test
+       void e06_listInterface_clear() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+               assertSize(2, list);
+
+               list.clear();
+               assertTrue(list.isEmpty());
+               assertSize(0, list);
+       }
+
+       @Test
+       void e07_listInterface_remove() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+
+               assertTrue(list.remove("value1"));
+               assertFalse(list.remove("value1"));  // Already removed
+               assertFalse(list.remove("nonexistent"));
+
+               assertSize(1, list);
+               assertEquals("value2", list.get(0));
+       }
+
+       @Test
+       void e08_listInterface_removeAtIndex() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+               list.add("value3");
+
+               assertEquals("value2", list.remove(1));
+               assertSize(2, list);
+               assertEquals("value1", list.get(0));
+               assertEquals("value3", list.get(1));
+       }
+
+       @Test
+       void e09_listInterface_set() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+
+               assertEquals("value2", list.set(1, "value3"));  // Updated
+               assertEquals("value3", list.get(1));
+
+               assertEquals("value3", list.set(1, null));  // Filtered out, 
returns existing
+               assertEquals("value3", list.get(1));  // Still has old value
+       }
+
+       
//====================================================================================================
+       // Edge cases - empty list
+       
//====================================================================================================
+
+       @Test
+       void f01_emptyList() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(list.isEmpty());
+               assertSize(0, list);
+               assertThrows(IndexOutOfBoundsException.class, () -> 
list.get(0));
+               assertFalse(list.contains("any"));
+       }
+
+       
//====================================================================================================
+       // Builder validation
+       
//====================================================================================================
+
+       @Test
+       void g01_builder_noFilter_acceptsAllElements() {
+               // Filter is optional - defaults to v -> true (accepts all 
elements)
+               var list = FilteredList.create(String.class)
+                       .build();
+
+               assertNotNull(list);
+               assertTrue(list.add("value1"));
+               assertTrue(list.add(null));  // Should be accepted (no filter)
+               assertTrue(list.add("value3"));
+
+               assertSize(3, list);
+               assertList(list, "value1", "<null>", "value3");
+       }
+
+       @Test
+       void g02_builder_nullFilter_throwsException() {
+               assertThrowsWithMessage(IllegalArgumentException.class, 
"value", () -> {
+                       FilteredList.create(String.class).filter(null);
+               });
+       }
+
+       @Test
+       void g03_builder_nullInner_throwsException() {
+               assertThrowsWithMessage(IllegalArgumentException.class, 
"value", () -> {
+                       FilteredList.create(String.class)
+                               .filter(v -> true)
+                               .inner(null);
+               });
+       }
+
+       
//====================================================================================================
+       // Builder - create() without parameters
+       
//====================================================================================================
+
+       @Test
+       void h01_builder_createWithoutParameters() {
+               var list = FilteredList
+                       .<String>create()
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(list.add("value1"));
+               assertFalse(list.add(null));  // Filtered out
+
+               assertSize(1, list);
+               assertEquals("value1", list.get(0));
+       }
+
+       @Test
+       void h02_builder_createWithoutParameters_usesObjectClass() {
+               var list = FilteredList
+                       .create()
+                       .filter(v -> v != null)
+                       .build();
+
+               // Object.class accepts any type
+               list.add("string");
+               list.add(123);
+               list.add(List.of(1, 2));
+
+               assertSize(3, list);
+               assertEquals("string", list.get(0));
+               assertEquals(123, list.get(1));
+               assertEquals(List.of(1, 2), list.get(2));
+       }
+
+       
//====================================================================================================
+       // add() method - type validation with types specified
+       
//====================================================================================================
+
+       @Test
+       void i01_add_withTypeValidation_validTypes() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertTrue(list.add(5));   // Valid types, added
+               assertTrue(list.add(10));  // Valid types, added
+               assertFalse(list.add(-1));  // Valid types, but filtered out
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+       }
+
+       @Test
+       void i02_add_withTypeValidation_invalidType() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertThrowsWithMessage(RuntimeException.class, "could not be 
converted to element type", () -> {
+                       list.addConverted("value");  // Invalid type (String 
instead of Integer)
+               });
+       }
+
+       @Test
+       void i03_add_withTypeValidation_noTypesSpecified() {
+               var list = FilteredList
+                       .<Object>create()
+                       .filter(v -> v != null)
+                       .build();
+
+               // Object.class is used when types not specified, which accepts 
any type
+               list.add("value1");
+               list.add(123);  // Different types, but Object.class accepts 
any type
+
+               assertSize(2, list);
+               assertEquals("value1", list.get(0));
+               assertEquals(123, list.get(1));
+       }
+
+       
//====================================================================================================
+       // add() method - with elementFunction
+       
//====================================================================================================
+
+       @Test
+       void j01_add_withElementFunction() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .elementFunction(o -> Integer.parseInt(o.toString()))
+                       .build();
+
+               assertTrue(list.addConverted("5"));   // Element converted from 
String to Integer
+               assertTrue(list.addConverted("10"));  // Element converted from 
String to Integer
+               assertFalse(list.addConverted("-1"));  // Element converted, 
but filtered out
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+       }
+
+       @Test
+       void j02_add_withElementFunction_noTypeSpecified() {
+               var list = FilteredList
+                       .<Integer>create()
+                       .filter(v -> v != null)
+                       .elementFunction(o -> Integer.parseInt(o.toString()))
+                       .build();
+
+               assertTrue(list.addConverted("123"));  // Element converted 
using function
+
+               assertSize(1, list);
+               assertEquals(123, list.get(0));
+       }
+
+       
//====================================================================================================
+       // addAll() method
+       
//====================================================================================================
+
+       @Test
+       void k01_addAll_withTypeValidation() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var source = List.of(5, -1, 10);
+
+               assertTrue(list.addAll(source));
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+       }
+
+       @Test
+       void k02_addAll_withElementFunction() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .elementFunction(o -> Integer.parseInt(o.toString()))
+                       .build();
+
+               var source = List.of("5", "-1", "10");
+
+               list.addAllConverted(source);
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+       }
+
+       @Test
+       void k03_addAll_nullSource() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.addAllConverted(null);  // Should be no-op
+
+               assertTrue(list.isEmpty());
+               assertSize(0, list);
+       }
+
+       @Test
+       void k04_addAll_emptySource() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertFalse(list.addAll(List.of()));  // Empty list
+
+               assertTrue(list.isEmpty());
+               assertSize(0, list);
+       }
+
+       @Test
+       void k05_addAll_atIndex() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               list.add(1);
+               list.add(5);
+
+               assertTrue(list.addAll(1, List.of(2, 3, -1, 4)));  // -1 
filtered out
+
+               assertSize(5, list);
+               assertEquals(1, list.get(0));
+               assertEquals(2, list.get(1));
+               assertEquals(3, list.get(2));
+               assertEquals(4, list.get(3));
+               assertEquals(5, list.get(4));
+       }
+
+       
//====================================================================================================
+       // add() method - return value
+       
//====================================================================================================
+
+       @Test
+       void l01_add_returnValue_newElement() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(list.add("value1"));  // New element, returns true
+       }
+
+       @Test
+       void l02_add_returnValue_filteredOut() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               assertFalse(list.add(null));  // Filtered out, returns false
+               assertEquals("value1", list.get(0));  // Old value still there
+       }
+
+       
//====================================================================================================
+       // add() method - edge cases with functions
+       
//====================================================================================================
+
+       @Test
+       void m01_add_elementFunctionThrowsException() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null)
+                       .elementFunction(o -> {
+                               if (o == null)
+                                       throw new 
IllegalArgumentException("Element cannot be null");
+                               return Integer.parseInt(o.toString());
+                       })
+                       .build();
+
+               assertThrows(IllegalArgumentException.class, () -> {
+                       list.addConverted(null);
+               });
+       }
+
+       @Test
+       void m02_add_elementFunctionReturnsNull() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .elementFunction(o -> {
+                               try {
+                                       return Integer.parseInt(o.toString());
+                               } catch (NumberFormatException e) {
+                                       return null;  // Return null for 
invalid numbers
+                               }
+                       })
+                       .build();
+
+               assertTrue(list.addConverted("5"));   // Valid, added
+               assertFalse(list.addConverted("abc")); // Function returns 
null, filtered out
+
+               assertSize(1, list);
+               assertEquals(5, list.get(0));
+       }
+
+       
//====================================================================================================
+       // wouldAccept() method
+       
//====================================================================================================
+
+       @Test
+       void n01_wouldAccept_returnsTrue() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertTrue(list.wouldAccept(5));
+               assertTrue(list.wouldAccept(10));
+       }
+
+       @Test
+       void n02_wouldAccept_returnsFalse() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertFalse(list.wouldAccept(null));
+               assertFalse(list.wouldAccept(-1));
+               assertFalse(list.wouldAccept(0));
+       }
+
+       @Test
+       void n03_wouldAccept_usedForPreValidation() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               // Pre-validate before adding
+               if (list.wouldAccept(5)) {
+                       list.add(5);
+               }
+               if (list.wouldAccept(-1)) {
+                       list.add(-1);
+               }
+
+               assertSize(1, list);
+               assertTrue(list.contains(5));
+               assertFalse(list.contains(-1));
+       }
+
+       
//====================================================================================================
+       // getFilter() method
+       
//====================================================================================================
+
+       @Test
+       void o01_getFilter_returnsFilter() {
+               var originalFilter = (Predicate<Integer>)(v -> v != null && v > 
0);
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(originalFilter)
+                       .build();
+
+               var retrievedFilter = list.getFilter();
+               assertNotNull(retrievedFilter);
+               // The filter may be combined with the default filter, so test 
behavior instead of instance equality
+               assertTrue(retrievedFilter.test(5));   // Should accept 
positive values
+               assertFalse(retrievedFilter.test(-1)); // Should reject 
negative values
+               assertFalse(retrievedFilter.test(null)); // Should reject null 
values
+       }
+
+       @Test
+       void o02_getFilter_canBeUsedForOtherPurposes() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var filter = list.getFilter();
+
+               // Use the filter independently
+               assertTrue(filter.test(5));
+               assertFalse(filter.test(-1));
+       }
+
+       
//====================================================================================================
+       // Null handling for primitive types
+       
//====================================================================================================
+
+       @Test
+       void p01_add_nullForPrimitiveType_throwsException() {
+               var list = FilteredList
+                       .create(int.class)
+                       .filter(v -> true)
+                       .build();
+
+               assertThrowsWithMessage(RuntimeException.class, "Cannot set 
null element for primitive type", () -> {
+                       list.addConverted(null);
+               });
+       }
+
+       @Test
+       void p02_add_nullForWrapperType_allowed() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> true)  // Accept all, including null
+                       .build();
+
+               assertTrue(list.add(null));  // Should work for wrapper types
+
+               assertSize(1, list);
+               assertNull(list.get(0));
+       }
+
+       
//====================================================================================================
+       // toString(), equals(), hashCode()
+       
//====================================================================================================
+
+       @Test
+       void q01_toString_delegatesToUnderlyingList() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               list.add("value1");
+               list.add("value2");
+
+               var underlyingList = new ArrayList<String>();
+               underlyingList.add("value1");
+               underlyingList.add("value2");
+
+               assertEquals(underlyingList.toString(), list.toString());
+       }
+
+       @Test
+       void q02_equals_delegatesToUnderlyingList() {
+               var list1 = FilteredList
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               list1.add("value1");
+               list1.add("value2");
+
+               var list2 = new ArrayList<String>();
+               list2.add("value1");
+               list2.add("value2");
+
+               assertTrue(list1.equals(list2));
+               assertTrue(list2.equals(list1));
+       }
+
+       @Test
+       void q03_equals_differentContents_returnsFalse() {
+               var list1 = FilteredList
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               list1.add("value1");
+
+               var list2 = new ArrayList<String>();
+               list2.add("value2");
+
+               assertFalse(list1.equals(list2));
+               assertFalse(list2.equals(list1));
+       }
+
+       @Test
+       void q04_hashCode_delegatesToUnderlyingList() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               list.add("value1");
+               list.add("value2");
+
+               var underlyingList = new ArrayList<String>();
+               underlyingList.add("value1");
+               underlyingList.add("value2");
+
+               assertEquals(underlyingList.hashCode(), list.hashCode());
+       }
+
+       
//====================================================================================================
+       // addAny() method
+       
//====================================================================================================
+
+       @Test
+       void r01_addAny_withCollections() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var list1 = List.of(5, -1);
+               var list2 = List.of(10);
+               list.addAny(list1, list2);  // Adds 5, 10 (-1 filtered out)
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+       }
+
+       @Test
+       void r02_addAny_withArrays() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var array1 = new Integer[]{5, -1};
+               var array2 = new Integer[]{10};
+               list.addAny(array1, array2);  // Adds 5, 10 (-1 filtered out)
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+       }
+
+       @Test
+       void r03_addAny_withMixedTypes() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               list.addAny(5, List.of(-1, 10), 15);  // Adds 5, 10, 15 (-1 
filtered out)
+
+               assertSize(3, list);
+               assertEquals(5, list.get(0));
+               assertEquals(10, list.get(1));
+               assertEquals(15, list.get(2));
+       }
+
+       @Test
+       void r04_addAny_nullValues() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.addAny("value1", null, "value2", null);  // nulls ignored
+
+               assertSize(2, list);
+               assertEquals("value1", list.get(0));
+               assertEquals("value2", list.get(1));
+       }
+
+       
//====================================================================================================
+       // Multiple filters
+       
//====================================================================================================
+
+       @Test
+       void t01_multipleFilters() {
+               var list = FilteredList
+                       .create(Integer.class)
+                       .filter(v -> v != null)           // First filter
+                       .filter(v -> v > 0)               // Second filter 
(ANDed with first)
+                       .filter(v -> v < 100)             // Third filter 
(ANDed with previous)
+                       .build();
+
+               assertTrue(list.add(5));   // Passes all filters
+               assertFalse(list.add(null)); // Fails first filter
+               assertFalse(list.add(-1));  // Fails second filter
+               assertFalse(list.add(0));   // Fails second filter
+               assertFalse(list.add(100)); // Fails third filter
+               assertTrue(list.add(50));   // Passes all filters
+
+               assertSize(2, list);
+               assertEquals(5, list.get(0));
+               assertEquals(50, list.get(1));
+       }
+
+       
//====================================================================================================
+       // ListIterator
+       
//====================================================================================================
+
+       @Test
+       void u01_listIterator() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+               list.add("value3");
+
+               var iterator = list.listIterator();
+               assertTrue(iterator.hasNext());
+               assertEquals("value1", iterator.next());
+               assertEquals("value2", iterator.next());
+               assertEquals("value3", iterator.next());
+               assertFalse(iterator.hasNext());
+       }
+
+       @Test
+       void u02_listIterator_atIndex() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+               list.add("value3");
+
+               var iterator = list.listIterator(1);
+               assertTrue(iterator.hasNext());
+               assertEquals("value2", iterator.next());
+               assertEquals("value3", iterator.next());
+               assertFalse(iterator.hasNext());
+       }
+
+       
//====================================================================================================
+       // subList
+       
//====================================================================================================
+
+       @Test
+       void v01_subList() {
+               var list = FilteredList
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               list.add("value1");
+               list.add("value2");
+               list.add("value3");
+               list.add("value4");
+
+               var subList = list.subList(1, 3);
+               assertSize(2, subList);
+               assertEquals("value2", subList.get(0));
+               assertEquals("value3", subList.get(1));
+       }
+}
+
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/FilteredSet_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/FilteredSet_Test.java
new file mode 100644
index 0000000000..fe8e3e2032
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/FilteredSet_Test.java
@@ -0,0 +1,948 @@
+/*
+ * 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.juneau.commons.collections;
+
+import static org.apache.juneau.TestUtils.assertThrowsWithMessage;
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.function.*;
+
+import org.apache.juneau.*;
+import org.junit.jupiter.api.*;
+
+class FilteredSet_Test extends TestBase {
+
+       
//====================================================================================================
+       // Basic filtering - filter out null values
+       
//====================================================================================================
+
+       @Test
+       void a01_filterNullValues_add() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(set.add("value1"));  // Added
+               assertFalse(set.add(null));      // Filtered out
+               assertTrue(set.add("value3"));  // Added
+
+               assertSize(2, set);
+               assertTrue(set.contains("value1"));
+               assertTrue(set.contains("value3"));
+               assertFalse(set.contains(null));
+       }
+
+       @Test
+       void a02_filterNullValues_addAll() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               var source = list("value1", null, "value3", null);
+
+               assertTrue(set.addAll(source));
+
+               assertSize(2, set);
+               assertTrue(set.contains("value1"));
+               assertTrue(set.contains("value3"));
+               assertFalse(set.contains(null));
+       }
+
+       @Test
+       void a03_filterNullValues_duplicateElements() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(set.add("value1"));  // Added
+               assertFalse(set.add("value1")); // Already present, returns 
false
+               assertTrue(set.add("value2"));  // Added
+
+               assertSize(2, set);
+               assertTrue(set.contains("value1"));
+               assertTrue(set.contains("value2"));
+       }
+
+       
//====================================================================================================
+       // Filter based on value - positive numbers only
+       
//====================================================================================================
+
+       @Test
+       void b01_filterPositiveNumbers() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertTrue(set.add(5));   // Added
+               assertFalse(set.add(-1));  // Filtered out
+               assertFalse(set.add(0));   // Filtered out
+               assertTrue(set.add(10));  // Added
+               assertFalse(set.add(null)); // Filtered out
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+               assertFalse(set.contains(-1));
+               assertFalse(set.contains(0));
+       }
+
+       
//====================================================================================================
+       // Filter based on string length
+       
//====================================================================================================
+
+       @Test
+       void c01_filterStringLength() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null && v.length() > 3)
+                       .build();
+
+               assertTrue(set.add("short"));   // Added
+               assertFalse(set.add("ab"));      // Filtered out (length <= 3)
+               assertTrue(set.add("longer"));  // Added
+               assertFalse(set.add(null));      // Filtered out
+
+               assertSize(2, set);
+               assertTrue(set.contains("short"));
+               assertTrue(set.contains("longer"));
+               assertFalse(set.contains("ab"));
+       }
+
+       
//====================================================================================================
+       // Custom set types - TreeSet
+       
//====================================================================================================
+
+       @Test
+       void d01_customSetType_TreeSet() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .inner(new TreeSet<>())
+                       .build();
+
+               set.add(5);
+               set.add(-1);  // Filtered out
+               set.add(10);
+               set.add(3);
+
+               // TreeSet maintains sorted order
+               var elements = new ArrayList<>(set);
+               assertEquals(List.of(3, 5, 10), elements);
+       }
+
+       
//====================================================================================================
+       // Custom set types - CopyOnWriteArraySet
+       
//====================================================================================================
+
+       @Test
+       void d02_customSetType_CopyOnWriteArraySet() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .inner(new CopyOnWriteArraySet<>())
+                       .build();
+
+               set.add("value1");
+               set.add(null);  // Filtered out
+               set.add("value3");
+
+               assertSize(2, set);
+       }
+
+       
//====================================================================================================
+       // Set interface methods - contains, containsAll
+       
//====================================================================================================
+
+       @Test
+       void e01_setInterface_contains() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add(null);  // Filtered out
+
+               assertTrue(set.contains("value1"));
+               assertFalse(set.contains(null));  // null was filtered out
+       }
+
+       @Test
+       void e02_setInterface_containsAll() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add("value2");
+               set.add("value3");
+
+               assertTrue(set.containsAll(List.of("value1", "value2")));
+               assertFalse(set.containsAll(List.of("value1", "value4")));
+               assertFalse(set.containsAll(Arrays.asList("value1", 
(String)null)));  // null was filtered out
+       }
+
+       @Test
+       void e03_setInterface_size() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertEquals(0, set.size());
+               set.add("value1");
+               assertEquals(1, set.size());
+               set.add(null);  // Filtered out
+               assertEquals(1, set.size());
+               set.add("value3");
+               assertEquals(2, set.size());
+               set.add("value1");  // Duplicate, not added
+               assertEquals(2, set.size());
+       }
+
+       @Test
+       void e04_setInterface_isEmpty() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(set.isEmpty());
+               set.add("value1");
+               assertFalse(set.isEmpty());
+       }
+
+       @Test
+       void e05_setInterface_clear() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add("value2");
+               assertSize(2, set);
+
+               set.clear();
+               assertTrue(set.isEmpty());
+               assertSize(0, set);
+       }
+
+       @Test
+       void e06_setInterface_remove() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add("value2");
+
+               assertTrue(set.remove("value1"));
+               assertFalse(set.remove("value1"));  // Already removed
+               assertFalse(set.remove("nonexistent"));
+
+               assertSize(1, set);
+               assertTrue(set.contains("value2"));
+       }
+
+       @Test
+       void e07_setInterface_removeAll() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add("value2");
+               set.add("value3");
+
+               assertTrue(set.removeAll(List.of("value1", "value2")));
+               assertSize(1, set);
+               assertTrue(set.contains("value3"));
+       }
+
+       @Test
+       void e08_setInterface_retainAll() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add("value2");
+               set.add("value3");
+
+               assertTrue(set.retainAll(List.of("value1", "value2")));
+               assertSize(2, set);
+               assertTrue(set.contains("value1"));
+               assertTrue(set.contains("value2"));
+               assertFalse(set.contains("value3"));
+       }
+
+       
//====================================================================================================
+       // Edge cases - empty set
+       
//====================================================================================================
+
+       @Test
+       void f01_emptySet() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(set.isEmpty());
+               assertSize(0, set);
+               assertFalse(set.contains("any"));
+       }
+
+       
//====================================================================================================
+       // Builder validation
+       
//====================================================================================================
+
+       @Test
+       void g01_builder_noFilter_acceptsAllElements() {
+               // Filter is optional - defaults to v -> true (accepts all 
elements)
+               var set = FilteredSet.create(String.class)
+                       .build();
+
+               assertNotNull(set);
+               assertTrue(set.add("value1"));
+               assertTrue(set.add(null));  // Should be accepted (no filter)
+               assertTrue(set.add("value3"));
+
+               assertSize(3, set);
+               assertTrue(set.contains("value1"));
+               assertTrue(set.contains(null));
+               assertTrue(set.contains("value3"));
+       }
+
+       @Test
+       void g02_builder_nullFilter_throwsException() {
+               assertThrowsWithMessage(IllegalArgumentException.class, 
"value", () -> {
+                       FilteredSet.create(String.class).filter(null);
+               });
+       }
+
+       @Test
+       void g03_builder_nullInner_throwsException() {
+               assertThrowsWithMessage(IllegalArgumentException.class, 
"value", () -> {
+                       FilteredSet.create(String.class)
+                               .filter(v -> true)
+                               .inner(null);
+               });
+       }
+
+       
//====================================================================================================
+       // Builder - create() without parameters
+       
//====================================================================================================
+
+       @Test
+       void h01_builder_createWithoutParameters() {
+               var set = FilteredSet
+                       .<String>create()
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(set.add("value1"));
+               assertFalse(set.add(null));  // Filtered out
+
+               assertSize(1, set);
+               assertTrue(set.contains("value1"));
+       }
+
+       @Test
+       void h02_builder_createWithoutParameters_usesObjectClass() {
+               var set = FilteredSet
+                       .create()
+                       .filter(v -> v != null)
+                       .build();
+
+               // Object.class accepts any type
+               set.add("string");
+               set.add(123);
+               set.add(List.of(1, 2));
+
+               assertSize(3, set);
+               assertTrue(set.contains("string"));
+               assertTrue(set.contains(123));
+               assertTrue(set.contains(List.of(1, 2)));
+       }
+
+       
//====================================================================================================
+       // add() method - type validation with types specified
+       
//====================================================================================================
+
+       @Test
+       void i01_add_withTypeValidation_validTypes() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertTrue(set.add(5));   // Valid types, added
+               assertTrue(set.add(10));  // Valid types, added
+               assertFalse(set.add(-1));  // Valid types, but filtered out
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+       }
+
+       @Test
+       void i02_add_withTypeValidation_invalidType() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertThrowsWithMessage(RuntimeException.class, "could not be 
converted to element type", () -> {
+                       set.addConverted("value");  // Invalid type (String 
instead of Integer)
+               });
+       }
+
+       @Test
+       void i03_add_withTypeValidation_noTypesSpecified() {
+               var set = FilteredSet
+                       .<Object>create()
+                       .filter(v -> v != null)
+                       .build();
+
+               // Object.class is used when types not specified, which accepts 
any type
+               set.add("value1");
+               set.add(123);  // Different types, but Object.class accepts any 
type
+
+               assertSize(2, set);
+               assertTrue(set.contains("value1"));
+               assertTrue(set.contains(123));
+       }
+
+       
//====================================================================================================
+       // add() method - with elementFunction
+       
//====================================================================================================
+
+       @Test
+       void j01_add_withElementFunction() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .elementFunction(o -> Integer.parseInt(o.toString()))
+                       .build();
+
+               assertTrue(set.addConverted("5"));   // Element converted from 
String to Integer
+               assertTrue(set.addConverted("10"));  // Element converted from 
String to Integer
+               assertFalse(set.addConverted("-1"));  // Element converted, but 
filtered out
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+       }
+
+       @Test
+       void j02_add_withElementFunction_noTypeSpecified() {
+               var set = FilteredSet
+                       .<Integer>create()
+                       .filter(v -> v != null)
+                       .elementFunction(o -> Integer.parseInt(o.toString()))
+                       .build();
+
+               assertTrue(set.addConverted("123"));  // Element converted 
using function
+
+               assertSize(1, set);
+               assertTrue(set.contains(123));
+       }
+
+       
//====================================================================================================
+       // addAll() method
+       
//====================================================================================================
+
+       @Test
+       void k01_addAll_withTypeValidation() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var source = List.of(5, -1, 10);
+
+               assertTrue(set.addAll(source));
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+       }
+
+       @Test
+       void k02_addAll_withElementFunction() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .elementFunction(o -> Integer.parseInt(o.toString()))
+                       .build();
+
+               var source = List.of("5", "-1", "10");
+
+               set.addAllConverted(source);
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+       }
+
+       @Test
+       void k03_addAll_nullSource() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.addAllConverted(null);  // Should be no-op
+
+               assertTrue(set.isEmpty());
+               assertSize(0, set);
+       }
+
+       @Test
+       void k04_addAll_emptySource() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertFalse(set.addAll(Set.of()));  // Empty set
+
+               assertTrue(set.isEmpty());
+               assertSize(0, set);
+       }
+
+       @Test
+       void k05_addAll_duplicateElements() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               set.add(5);
+               assertTrue(set.addAll(List.of(5, 10)));  // 5 already present, 
10 added - returns true because set was modified
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+       }
+
+       
//====================================================================================================
+       // add() method - return value
+       
//====================================================================================================
+
+       @Test
+       void l01_add_returnValue_newElement() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               assertTrue(set.add("value1"));  // New element, returns true
+       }
+
+       @Test
+       void l02_add_returnValue_duplicateElement() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               assertFalse(set.add("value1"));  // Duplicate, returns false
+               assertSize(1, set);
+       }
+
+       @Test
+       void l03_add_returnValue_filteredOut() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               assertFalse(set.add(null));  // Filtered out, returns false
+               assertTrue(set.contains("value1"));  // Old value still there
+       }
+
+       
//====================================================================================================
+       // add() method - edge cases with functions
+       
//====================================================================================================
+
+       @Test
+       void m01_add_elementFunctionThrowsException() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null)
+                       .elementFunction(o -> {
+                               if (o == null)
+                                       throw new 
IllegalArgumentException("Element cannot be null");
+                               return Integer.parseInt(o.toString());
+                       })
+                       .build();
+
+               assertThrows(IllegalArgumentException.class, () -> {
+                       set.addConverted(null);
+               });
+       }
+
+       @Test
+       void m02_add_elementFunctionReturnsNull() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .elementFunction(o -> {
+                               try {
+                                       return Integer.parseInt(o.toString());
+                               } catch (NumberFormatException e) {
+                                       return null;  // Return null for 
invalid numbers
+                               }
+                       })
+                       .build();
+
+               assertTrue(set.addConverted("5"));   // Valid, added
+               assertFalse(set.addConverted("abc")); // Function returns null, 
filtered out
+
+               assertSize(1, set);
+               assertTrue(set.contains(5));
+       }
+
+       
//====================================================================================================
+       // wouldAccept() method
+       
//====================================================================================================
+
+       @Test
+       void n01_wouldAccept_returnsTrue() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertTrue(set.wouldAccept(5));
+               assertTrue(set.wouldAccept(10));
+       }
+
+       @Test
+       void n02_wouldAccept_returnsFalse() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               assertFalse(set.wouldAccept(null));
+               assertFalse(set.wouldAccept(-1));
+               assertFalse(set.wouldAccept(0));
+       }
+
+       @Test
+       void n03_wouldAccept_usedForPreValidation() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               // Pre-validate before adding
+               if (set.wouldAccept(5)) {
+                       set.add(5);
+               }
+               if (set.wouldAccept(-1)) {
+                       set.add(-1);
+               }
+
+               assertSize(1, set);
+               assertTrue(set.contains(5));
+               assertFalse(set.contains(-1));
+       }
+
+       
//====================================================================================================
+       // getFilter() method
+       
//====================================================================================================
+
+       @Test
+       void o01_getFilter_returnsFilter() {
+               var originalFilter = (Predicate<Integer>)(v -> v != null && v > 
0);
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(originalFilter)
+                       .build();
+
+               var retrievedFilter = set.getFilter();
+               assertNotNull(retrievedFilter);
+               // The filter may be combined with the default filter, so test 
behavior instead of instance equality
+               assertTrue(retrievedFilter.test(5));   // Should accept 
positive values
+               assertFalse(retrievedFilter.test(-1)); // Should reject 
negative values
+               assertFalse(retrievedFilter.test(null)); // Should reject null 
values
+       }
+
+       @Test
+       void o02_getFilter_canBeUsedForOtherPurposes() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var filter = set.getFilter();
+
+               // Use the filter independently
+               assertTrue(filter.test(5));
+               assertFalse(filter.test(-1));
+       }
+
+       
//====================================================================================================
+       // Null handling for primitive types
+       
//====================================================================================================
+
+       @Test
+       void p01_add_nullForPrimitiveType_throwsException() {
+               var set = FilteredSet
+                       .create(int.class)
+                       .filter(v -> true)
+                       .build();
+
+               assertThrowsWithMessage(RuntimeException.class, "Cannot set 
null element for primitive type", () -> {
+                       set.addConverted(null);
+               });
+       }
+
+       @Test
+       void p02_add_nullForWrapperType_allowed() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> true)  // Accept all, including null
+                       .build();
+
+               assertTrue(set.add(null));  // Should work for wrapper types
+
+               assertSize(1, set);
+               assertTrue(set.contains(null));
+       }
+
+       
//====================================================================================================
+       // toString(), equals(), hashCode()
+       
//====================================================================================================
+
+       @Test
+       void q01_toString_delegatesToUnderlyingSet() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               set.add("value1");
+               set.add("value2");
+
+               var underlyingSet = new LinkedHashSet<String>();
+               underlyingSet.add("value1");
+               underlyingSet.add("value2");
+
+               assertEquals(underlyingSet.toString(), set.toString());
+       }
+
+       @Test
+       void q02_equals_delegatesToUnderlyingSet() {
+               var set1 = FilteredSet
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               set1.add("value1");
+               set1.add("value2");
+
+               var set2 = new LinkedHashSet<String>();
+               set2.add("value1");
+               set2.add("value2");
+
+               assertTrue(set1.equals(set2));
+               assertTrue(set2.equals(set1));
+       }
+
+       @Test
+       void q03_equals_differentContents_returnsFalse() {
+               var set1 = FilteredSet
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               set1.add("value1");
+
+               var set2 = new LinkedHashSet<String>();
+               set2.add("value2");
+
+               assertFalse(set1.equals(set2));
+               assertFalse(set2.equals(set1));
+       }
+
+       @Test
+       void q04_hashCode_delegatesToUnderlyingSet() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> true)
+                       .build();
+               set.add("value1");
+               set.add("value2");
+
+               var underlyingSet = new LinkedHashSet<String>();
+               underlyingSet.add("value1");
+               underlyingSet.add("value2");
+
+               assertEquals(underlyingSet.hashCode(), set.hashCode());
+       }
+
+       
//====================================================================================================
+       // addAny() method
+       
//====================================================================================================
+
+       @Test
+       void r01_addAny_withCollections() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var set1 = Set.of(5, -1);
+               var set2 = Set.of(10);
+               set.addAny(set1, set2);  // Adds 5, 10 (-1 filtered out)
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+       }
+
+       @Test
+       void r02_addAny_withArrays() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               var array1 = new Integer[]{5, -1};
+               var array2 = new Integer[]{10};
+               set.addAny(array1, array2);  // Adds 5, 10 (-1 filtered out)
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+       }
+
+       @Test
+       void r03_addAny_withMixedTypes() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null && v > 0)
+                       .build();
+
+               set.addAny(5, Set.of(-1, 10), 15);  // Adds 5, 10, 15 (-1 
filtered out)
+
+               assertSize(3, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(10));
+               assertTrue(set.contains(15));
+       }
+
+       @Test
+       void r04_addAny_nullValues() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.addAny("value1", null, "value2", null);  // nulls ignored
+
+               assertSize(2, set);
+               assertTrue(set.contains("value1"));
+               assertTrue(set.contains("value2"));
+       }
+
+       
//====================================================================================================
+       // Multiple filters
+       
//====================================================================================================
+
+       @Test
+       void t01_multipleFilters() {
+               var set = FilteredSet
+                       .create(Integer.class)
+                       .filter(v -> v != null)           // First filter
+                       .filter(v -> v > 0)               // Second filter 
(ANDed with first)
+                       .filter(v -> v < 100)             // Third filter 
(ANDed with previous)
+                       .build();
+
+               assertTrue(set.add(5));   // Passes all filters
+               assertFalse(set.add(null)); // Fails first filter
+               assertFalse(set.add(-1));  // Fails second filter
+               assertFalse(set.add(0));   // Fails second filter
+               assertFalse(set.add(100)); // Fails third filter
+               assertTrue(set.add(50));   // Passes all filters
+
+               assertSize(2, set);
+               assertTrue(set.contains(5));
+               assertTrue(set.contains(50));
+       }
+
+       
//====================================================================================================
+       // Iterator
+       
//====================================================================================================
+
+       @Test
+       void u01_iterator() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add("value2");
+               set.add("value3");
+
+               var iterator = set.iterator();
+               var found = new HashSet<String>();
+               while (iterator.hasNext()) {
+                       found.add(iterator.next());
+               }
+
+               assertSize(3, found);
+               assertTrue(found.contains("value1"));
+               assertTrue(found.contains("value2"));
+               assertTrue(found.contains("value3"));
+       }
+
+       @Test
+       void u02_iterator_remove() {
+               var set = FilteredSet
+                       .create(String.class)
+                       .filter(v -> v != null)
+                       .build();
+
+               set.add("value1");
+               set.add("value2");
+               set.add("value3");
+
+               var iterator = set.iterator();
+               iterator.next();
+               iterator.remove();
+
+               assertSize(2, set);
+       }
+}
+
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/MapBuilder_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/MapBuilder_Test.java
index f78cb25392..090405f55e 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/MapBuilder_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/MapBuilder_Test.java
@@ -68,7 +68,12 @@ class MapBuilder_Test extends TestBase {
                        .add("b", 2)
                        .build();
 
-               assertMap(map, "a=1", "x=10", "y=20", "b=2");
+               // Without ordered(), order is not guaranteed (HashMap)
+               assertSize(4, map);
+               assertEquals(1, map.get("a"));
+               assertEquals(10, map.get("x"));
+               assertEquals(20, map.get("y"));
+               assertEquals(2, map.get("b"));
        }
 
        @Test
@@ -101,6 +106,88 @@ class MapBuilder_Test extends TestBase {
                assertThrows(IllegalArgumentException.class, () -> 
b.addPairs("a", "b", "c"));
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Ordered
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Test
+       void c00_ordered_preservesInsertionOrder() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .ordered()
+                       .add("c", 3)
+                       .add("a", 1)
+                       .add("b", 2)
+                       .build();
+
+               assertSize(3, map);
+               assertTrue(map instanceof LinkedHashMap);
+               // With ordered(), insertion order is preserved
+               var keys = new ArrayList<>(map.keySet());
+               assertEquals("c", keys.get(0));
+               assertEquals("a", keys.get(1));
+               assertEquals("b", keys.get(2));
+       }
+
+       @Test
+       void c00b_ordered_defaultIsHashMap() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .add("a", 1)
+                       .add("b", 2)
+                       .build();
+
+               assertSize(2, map);
+               // Without ordered(), should be HashMap (unordered)
+               assertTrue(map instanceof HashMap);
+               assertFalse(map instanceof LinkedHashMap);
+               // Verify it's not ordered by checking key order is not 
preserved
+               // (HashMap doesn't guarantee order, so we can't rely on it)
+               assertEquals(1, map.get("a"));
+               assertEquals(2, map.get("b"));
+       }
+
+       @Test
+       void c00c_ordered_boolean() {
+               var map1 = MapBuilder.create(String.class, Integer.class)
+                       .ordered(true)
+                       .add("a", 1)
+                       .build();
+               assertTrue(map1 instanceof LinkedHashMap);
+
+               var map2 = MapBuilder.create(String.class, Integer.class)
+                       .ordered(false)
+                       .add("a", 1)
+                       .build();
+               assertTrue(map2 instanceof HashMap);
+       }
+
+       @Test
+       void c00d_ordered_lastOneWins() {
+               // If ordered() is called first, then sorted(), sorted() wins
+               var map1 = MapBuilder.create(String.class, Integer.class)
+                       .ordered()
+                       .add("c", 3)
+                       .add("a", 1)
+                       .add("b", 2)
+                       .sorted()
+                       .build();
+               assertTrue(map1 instanceof TreeMap);
+               assertList(map1.keySet(), "a", "b", "c");
+
+               // If sorted() is called first, then ordered(), ordered() wins
+               var map2 = MapBuilder.create(String.class, Integer.class)
+                       .sorted()
+                       .add("c", 3)
+                       .add("a", 1)
+                       .add("b", 2)
+                       .ordered()
+                       .build();
+               assertTrue(map2 instanceof LinkedHashMap);
+               var keys2 = new ArrayList<>(map2.keySet());
+               assertEquals("c", keys2.get(0));
+               assertEquals("a", keys2.get(1));
+               assertEquals("b", keys2.get(2));
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // Sorting
        
//-----------------------------------------------------------------------------------------------------------------
@@ -211,21 +298,6 @@ class MapBuilder_Test extends TestBase {
                assertNotSame(original, map);  // After copy(), they're 
different maps
        }
 
-       @Test
-       void f02_noCopy() {
-               var original = new LinkedHashMap<String,Integer>();
-               original.put("a", 1);
-
-               var map = MapBuilder.create(String.class, Integer.class)
-                       .to(original)
-                       .add("b", 2)
-                       .build();
-
-               assertSize(2, map);
-               assertSize(2, original);  // Original modified
-               assertSame(original, map);
-       }
-
        
//-----------------------------------------------------------------------------------------------------------------
        // Complex scenarios
        
//-----------------------------------------------------------------------------------------------------------------
@@ -248,6 +320,29 @@ class MapBuilder_Test extends TestBase {
                assertList(map.keySet(), "a", "b", "x", "y");
        }
 
+       @Test
+       void g01b_multipleOperations_withOrdered() {
+               var existing = new LinkedHashMap<String,Integer>();
+               existing.put("x", 10);
+               existing.put("y", 20);
+
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .ordered()
+                       .add("a", 1)
+                       .addAll(existing)
+                       .add("b", 2)
+                       .build();
+
+               assertSize(4, map);
+               assertTrue(map instanceof LinkedHashMap);
+               // With ordered(), insertion order is preserved
+               var keys = new ArrayList<>(map.keySet());
+               assertEquals("a", keys.get(0));
+               assertEquals("x", keys.get(1));
+               assertEquals("y", keys.get(2));
+               assertEquals("b", keys.get(3));
+       }
+
        @Test
        void g02_sortedAndUnmodifiable() {
                var map = MapBuilder.create(String.class, Integer.class)
@@ -330,21 +425,6 @@ class MapBuilder_Test extends TestBase {
                assertMap(map, "a=3");  // Last value wins
        }
 
-       @Test
-       void h05_toExistingMap() {
-               var existing = new LinkedHashMap<String,Integer>();
-               existing.put("x", 10);
-
-               var map = MapBuilder.create(String.class, Integer.class)
-                       .to(existing)
-                       .add("y", 20)
-                       .build();
-
-               assertSize(2, map);
-               assertMap(map, "x=10", "y=20");
-               assertSame(existing, map);
-       }
-
        
//-----------------------------------------------------------------------------------------------------------------
        // Filtering
        
//-----------------------------------------------------------------------------------------------------------------
@@ -643,6 +723,82 @@ class MapBuilder_Test extends TestBase {
                });
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Concurrent
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Test
+       void o01_concurrent_defaultHashMap() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .concurrent()
+                       .add("a", 1)
+                       .add("b", 2)
+                       .build();
+
+               assertSize(2, map);
+               assertTrue(map instanceof 
java.util.concurrent.ConcurrentHashMap);
+       }
+
+       @Test
+       void o02_concurrent_ordered() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .ordered()
+                       .concurrent()
+                       .add("a", 1)
+                       .add("b", 2)
+                       .build();
+
+               assertSize(2, map);
+               // Should be a synchronized LinkedHashMap - verify by checking 
order is preserved
+               var keys = new ArrayList<>(map.keySet());
+               assertEquals("a", keys.get(0));
+               assertEquals("b", keys.get(1));
+               // Verify it's synchronized by checking it's not a 
ConcurrentHashMap
+               assertFalse(map instanceof 
java.util.concurrent.ConcurrentHashMap);
+       }
+
+       @Test
+       void o03_concurrent_sorted() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .sorted()
+                       .concurrent()
+                       .add("c", 3)
+                       .add("a", 1)
+                       .add("b", 2)
+                       .build();
+
+               assertSize(3, map);
+               assertTrue(map instanceof 
java.util.concurrent.ConcurrentSkipListMap);
+               assertList(map.keySet(), "a", "b", "c");
+       }
+
+       @Test
+       void o04_concurrent_boolean() {
+               var map1 = MapBuilder.create(String.class, Integer.class)
+                       .concurrent(true)
+                       .add("a", 1)
+                       .build();
+               assertTrue(map1 instanceof 
java.util.concurrent.ConcurrentHashMap);
+
+               var map2 = MapBuilder.create(String.class, Integer.class)
+                       .concurrent(false)
+                       .add("a", 1)
+                       .build();
+               assertTrue(map2 instanceof HashMap);
+       }
+
+       @Test
+       void o05_concurrent_unmodifiable() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .concurrent()
+                       .unmodifiable()
+                       .add("a", 1)
+                       .build();
+
+               assertSize(1, map);
+               assertThrows(UnsupportedOperationException.class, () -> 
map.put("b", 2));
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // Build edge cases
        
//-----------------------------------------------------------------------------------------------------------------
@@ -688,6 +844,29 @@ class MapBuilder_Test extends TestBase {
                assertEmpty(map);
        }
 
+       @Test
+       void l06_build_orderedWithNullMap() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .ordered()
+                       .build();
+
+               assertNotNull(map);
+               assertTrue(map instanceof LinkedHashMap);
+               assertEmpty(map);
+       }
+
+       @Test
+       void l07_build_defaultHashMap() {
+               var map = MapBuilder.create(String.class, Integer.class)
+                       .build();
+
+               assertNotNull(map);
+               // Without ordered(), should be HashMap (unordered)
+               assertTrue(map instanceof HashMap);
+               assertFalse(map instanceof LinkedHashMap);
+               assertEmpty(map);
+       }
+
        @Test
        void l05_build_unmodifiableWithNullMap() {
                var map = MapBuilder.create(String.class, Integer.class)

Reply via email to