Class MultiList<E>

java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
org.apache.juneau.commons.collections.MultiList<E>
Type Parameters:
E - The element type of this list.
All Implemented Interfaces:
Iterable<E>, Collection<E>, List<E>

public class MultiList<E> extends AbstractList<E>
A composite list that presents multiple lists as a single unified list.

This class allows multiple lists to be viewed and accessed as if they were merged into a single list, without actually copying the elements. Modifications made through the iterator or list iterator affect the underlying lists.

Features:
  • Zero-Copy Composition: No data is copied when creating a MultiList; it simply wraps the provided lists
  • Transparent Access: Accessing elements by index or iterating over a MultiList seamlessly traverses all underlying lists in order
  • Modification Support: Elements can be removed via the iterator's Iterator.remove() method
  • Efficient Size Calculation: The size is computed by summing the sizes of all underlying lists
  • Enumeration Support: Provides an Enumeration view via enumerator()
Usage:

// Create a MultiList from three separate lists List<String> list1 = List.of("a", "b"); List<String> list2 = List.of("c", "d"); List<String> list3 = List.of("e", "f"); MultiList<String> multiList = new MultiList<>(list1, list2, list3); // Access elements by index multiList.get(0); // Returns "a" multiList.get(3); // Returns "d" multiList.get(5); // Returns "f" // Iterate over all elements from all lists for (String element : multiList) { System.out.println(element); // Prints: a, b, c, d, e, f } // Get total size across all lists int totalSize = multiList.size(); // Returns: 6 // Remove elements via iterator (affects underlying lists) Iterator<String> it = multiList.iterator(); while (it.hasNext()) { if (it.next().equals("b")) { it.remove(); // Removes "b" from list1 } }

Behavior Notes:
Thread Safety:

This class is not inherently thread-safe. If the underlying lists are modified concurrently during iteration or access, the behavior is undefined. Synchronization must be handled externally if needed.

Example - Processing Multiple Data Sources:

// Combine results from database, cache, and defaults List<User> dbUsers = fetchFromDatabase(); List<User> cachedUsers = getCachedUsers(); List<User> defaultUsers = getDefaultUsers(); MultiList<User> allUsers = new MultiList<>(dbUsers, cachedUsers, defaultUsers); // Process all users from all sources allUsers.forEach(user -> processUser(user)); // Access specific user by index User user = allUsers.get(10);

See Also:
  • Constructor Details

    • MultiList

      @SafeVarargs public MultiList(List<E>... c)
      Creates a new MultiList that presents the specified lists as a single unified list.

      The lists are stored by reference (not copied), so modifications made through the MultiList's iterator will affect the original lists.

      Example:

      List<String> list1 = new ArrayList<>(List.of("a", "b")); List<String> list2 = new ArrayList<>(List.of("c", "d")); MultiList<String> multiList = new MultiList<>(list1, list2); // multiList now represents all elements from both lists

      Parameters:
      c - Zero or more lists to combine into this list. Must not be null, and no individual list can be null (but lists can be empty).
      Throws:
      IllegalArgumentException - if the lists array or any list within it is null.
  • Method Details

    • enumerator

      Returns an Enumeration view of this list.

      This is useful for compatibility with legacy APIs that require an Enumeration rather than an Iterator.

      Example:

      MultiList<String> multiList = new MultiList<>(list1, list2); Enumeration<String> enumeration = multiList.enumerator(); while (enumeration.hasMoreElements()) { String element = enumeration.nextElement(); // Process element }

      Returns:
      An Enumeration that iterates over all elements in all underlying lists.
      See Also:
    • get

      public E get(int index)
      Returns the element at the specified position in this list.

      The index is resolved by traversing the underlying lists in order until the correct list and position within that list is found.

      Example:

      List<String> list1 = List.of("a", "b"); // indices 0-1 List<String> list2 = List.of("c", "d", "e"); // indices 2-4 MultiList<String> multiList = new MultiList<>(list1, list2); multiList.get(0); // Returns "a" multiList.get(2); // Returns "c" multiList.get(4); // Returns "e"

      Specified by:
      get in interface List<E>
      Specified by:
      get in class AbstractList<E>
      Parameters:
      index - The index of the element to return.
      Returns:
      The element at the specified position.
      Throws:
      IndexOutOfBoundsException - if the index is out of range (index < 0 || index >= size()).
    • iterator

      public Iterator<E> iterator()
      Returns an iterator over all elements in all underlying lists.

      The iterator traverses each list in the order they were provided to the constructor. Within each list, the iteration order follows the list's natural order.

      The returned iterator supports the Iterator.remove() operation, which removes the current element from its underlying list.

      Behavior:
      Example:

      List<String> list1 = new ArrayList<>(List.of("a", "b")); List<String> list2 = new ArrayList<>(List.of("c", "d")); MultiList<String> multiList = new MultiList<>(list1, list2); Iterator<String> it = multiList.iterator(); while (it.hasNext()) { String element = it.next(); if (element.equals("b")) { it.remove(); // Removes "b" from list1 } }

      Specified by:
      iterator in interface Collection<E>
      Specified by:
      iterator in interface Iterable<E>
      Specified by:
      iterator in interface List<E>
      Overrides:
      iterator in class AbstractList<E>
      Returns:
      An iterator over all elements in all underlying lists.
    • listIterator

      Returns a list iterator over all elements in all underlying lists.

      The list iterator traverses each list in the order they were provided to the constructor. The iterator starts at the beginning of the first list.

      The returned list iterator supports the ListIterator.remove() operation, which removes the current element from its underlying list.

      Behavior:
      • Elements from the first list are iterated first, then the second, and so on
      • If a list is empty, it is skipped during iteration
      • Calling ListIterator.remove() removes the element from the underlying list
      • Bidirectional navigation is supported, but may be less efficient than forward-only iteration
      Specified by:
      listIterator in interface List<E>
      Overrides:
      listIterator in class AbstractList<E>
      Returns:
      A list iterator over all elements in all underlying lists, starting at the beginning.
    • listIterator

      public ListIterator<E> listIterator(int index)
      Returns a list iterator over all elements in all underlying lists, starting at the specified position.

      The list iterator traverses each list in the order they were provided to the constructor. The iterator starts at the specified index.

      Specified by:
      listIterator in interface List<E>
      Overrides:
      listIterator in class AbstractList<E>
      Parameters:
      index - The index to start the iterator at.
      Returns:
      A list iterator over all elements in all underlying lists, starting at the specified index.
      Throws:
      IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size()).
    • size

      public int size()
      Returns the total number of elements across all underlying lists.

      This method computes the size by summing the List.size() of each underlying list. The size is recalculated each time this method is called (it is not cached).

      Example:

      List<String> list1 = List.of("a", "b"); // size = 2 List<String> list2 = List.of("c", "d", "e"); // size = 3 MultiList<String> multiList = new MultiList<>(list1, list2); int totalSize = multiList.size(); // Returns: 5

      Specified by:
      size in interface Collection<E>
      Specified by:
      size in interface List<E>
      Specified by:
      size in class AbstractCollection<E>
      Returns:
      The sum of sizes of all underlying lists.
    • toString

      public String toString()
      Returns a string representation of this MultiList.

      The format is "[[...],[...],...]" where each [...] is the standard standard string representation of each underlying list (as returned by Object.toString()).

      Example:

      List<String> list1 = List.of("a", "b"); List<String> list2 = List.of("c", "d"); MultiList<String> multiList = new MultiList<>(list1, list2); multiList.toString(); // Returns: "[[a, b], [c, d]]"

      Overrides:
      toString in class AbstractCollection<E>
      Returns:
      A string representation of this MultiList.
    • equals

      public boolean equals(Object o)
      Compares the specified object with this list for equality.

      Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. In other words, two lists are defined to be equal if they contain the same elements in the same order.

      This implementation first checks if the specified object is this list. If so, it returns true; if not, it checks if the specified object is a list. If not, it returns false; if so, it iterates over both lists, comparing corresponding pairs of elements.

      Specified by:
      equals in interface Collection<E>
      Specified by:
      equals in interface List<E>
      Overrides:
      equals in class AbstractList<E>
      Parameters:
      o - The object to be compared for equality with this list.
      Returns:
      true if the specified object is equal to this list.
    • hashCode

      public int hashCode()
      Returns the hash code value for this list.

      The hash code of a list is defined to be the result of the following calculation:

      int hashCode = 1; for (E e : list) hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());

      This ensures that list1.equals(list2) implies that list1.hashCode()==list2.hashCode() for any two lists list1 and list2, as required by the general contract of Object.hashCode().

      Specified by:
      hashCode in interface Collection<E>
      Specified by:
      hashCode in interface List<E>
      Overrides:
      hashCode in class AbstractList<E>
      Returns:
      The hash code value for this list.