# How to Convert Properties to Collections (List, Set, and Map) in Owner

> Learn how to convert properties to collections List Set and Map in Owner. Discover Owner's automatic type conversion for Lists and Sets and how to implement custom Converters for Maps.

- Repository: [Matteo Baccan/owner](https://github.com/matteobaccan/owner)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Owner automatically converts comma-separated string properties into Java Collections like List and Set through its type-conversion engine in [`Converters.java`](https://github.com/matteobaccan/owner/blob/main/Converters.java), while Map types require a custom `Converter` implementation.**

The Owner configuration library by matteobaccan/owner maps plain String properties to strongly-typed Java objects through a sophisticated conversion system. When you need to convert properties to collections such as `List<String>` or `Set<Integer>`, the framework handles tokenization, type coercion, and instantiation automatically via the classes in `org.aeonbits.owner`.

## Converting Properties to List and Set Collections

Owner detects Collection return types and routes them through the `COLLECTION` converter defined in `org.aeonbits.owner.Converters`.

### The COLLECTION Converter Logic

When a method returns a type that implements `java.util.Collection`, the converter executes a four-step process:

1. **Type Detection**: The converter first verifies `Collection.class.isAssignableFrom(targetType)` (lines 66-70 in [`Converters.java`](https://github.com/matteobaccan/owner/blob/main/Converters.java)) to confirm the return type is a collection interface or implementation.
2. **Tokenization**: The raw property string is split using the tokenizer defined for the method (defaults to comma) according to the `@Separator` annotation.
3. **Array Conversion**: The resulting tokens are converted to an array of the target element type using the `ARRAY` converter.
4. **Collection Wrapping**: The array is wrapped via `Arrays.asList()` and then used to populate an instantiated concrete collection type.

This logic is implemented between lines 71 and 76, where the framework handles the transition from String tokens to typed collections.

### Generic Type Resolution

For parameterized types like `List<String>` or `Set<Integer>`, Owner extracts the generic argument from the method's return type to determine the element conversion target. If the method is raw (declared without generics), the framework falls back to `String.class` as the element type (lines 84-90). This extraction occurs in the type resolution phase before conversion begins.

### Concrete Collection Instantiation

The `instantiateCollection` method in [`Converters.java`](https://github.com/matteobaccan/owner/blob/main/Converters.java) handles the creation of the actual collection instance:

```java
private <T> Collection<T> instantiateCollection(Class<? extends T> targetType) {
    if (targetType.isInterface())
        return instantiateCollectionFromInterface(targetType);
    return instantiateCollectionFromClass(targetType);
}

```

If the return type is an interface such as `List`, `Set`, or `SortedSet`, Owner supplies a sensible default implementation—typically `ArrayList` for `List` and `LinkedHashSet` for `Set` (lines 94-115). When the return type is a concrete class, the framework attempts to invoke its no-argument constructor (lines 99-103).

### Practical Examples

Define a configuration interface with collection return types:

```java
import org.aeonbits.owner.Config;
import org.aeonbits.owner.ConfigFactory;
import java.util.List;
import java.util.Set;

public interface AppConfig extends Config {
    @Separator(",")
    @DefaultValue("a,b,c")
    List<String> names();

    @Separator(";")
    @DefaultValue("1;2;3")
    Set<Integer> ids();
}

```

Create and use the configuration:

```java
AppConfig cfg = ConfigFactory.create(AppConfig.class);

List<String> names = cfg.names();  // Returns ["a", "b", "c"]
Set<Integer> ids = cfg.ids();      // Returns [1, 2, 3]

```

## Working with Arrays of Collections

Owner supports arrays of collections through the same conversion pipeline. When a method returns `List<String>[]`, the `COLLECTION` converter processes each array element individually, applying the standard list conversion logic to every component.

```java
public interface ArrayConfig extends Config {
    @Separator("|")
    @DefaultValue("a|b|c;d|e|f")
    List<String>[] rows();
}

```

The outer array is handled by the standard array converter, while each inner list goes through the collection instantiation process described above.

## Converting Properties to Map Types

Unlike List and Set, Owner does **not** provide a generic Map converter because key/value types require arbitrary parsing logic that cannot be inferred from property strings alone. You must implement a custom `Converter` and register it with `@ConverterClass`.

### Implementing a Map Converter

Create a converter class that parses your specific map format:

```java
import org.aeonbits.owner.Converter;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

public class MapPropertyConverter implements Converter<Map<String, String>> {
    public Map<String, String> convert(Method method, String input) {
        Map<String, String> result = new LinkedHashMap<>();
        for (String pair : input.split(",")) {
            String[] kv = pair.split(":");
            result.put(kv[0].trim(), kv[1].trim());
        }
        return result;
    }
}

```

Apply the converter to your configuration method:

```java
public interface MapConfig extends Config {
    @Separator(";")
    @DefaultValue("host:localhost,port:8080;host:example.com,port:9090")
    @ConverterClass(MapPropertyConverter.class)
    Map<String, String>[] servers();
}

```

Owner treats the returned `Map` array like any other collection, with the `COLLECTION` converter managing the outer array structure while your custom converter handles the individual Map instances.

## Using the Collections Utility for Testing

Owner ships with `org.aeonbits.owner.util.Collections`, a helper class for creating immutable collection literals in test code or default value construction. These methods wrap `Arrays.asList` and `LinkedHashSet` for convenience (lines 91-96).

```java
import static org.aeonbits.owner.util.Collections.*;

@Test
public void testDefaults() {
    assertEquals(list("foo", "bar"), cfg.names());
    assertEquals(set(1, 2, 3), cfg.ids());
    assertEquals(map(entry("k", "v")), cfg.singleEntry());
}

```

The utility provides `list()`, `set()`, `map()`, and `entry()` methods that correspond to the standard Java collections used internally by the framework.

## Summary

- **Automatic Collection Conversion**: Owner converts comma-separated properties to `List`, `Set`, and other Collection types automatically when methods return these interfaces.
- **Type Safety**: The framework extracts generic type parameters in [`Converters.java`](https://github.com/matteobaccan/owner/blob/main/Converters.java) to ensure elements are converted from Strings to the correct target types (e.g., `Integer`, `Boolean`).
- **Interface Defaults**: When returning collection interfaces, Owner instantiates sensible defaults like `ArrayList` for `List` and `LinkedHashSet` for `Set` via `instantiateCollectionFromInterface`.
- **Custom Map Support**: Map types require custom `Converter` implementations using `@ConverterClass` because key/value parsing logic varies by use case.
- **Testing Utilities**: The `Collections` utility class in `org.aeonbits.owner.util.Collections` provides convenient literal constructors for unit testing configuration defaults.

## Frequently Asked Questions

### How does Owner determine which collection implementation to use?

When a method returns a collection interface such as `List` or `Set`, Owner invokes `instantiateCollectionFromInterface` in [`Converters.java`](https://github.com/matteobaccan/owner/blob/main/Converters.java) (lines 94-115) to select a default implementation. If the return type is a concrete class with a no-argument constructor, Owner instantiates that class directly via reflection (lines 99-103).

### Can I use a custom delimiter for collection properties?

Yes. Apply the `@Separator` annotation to your configuration method to define a custom tokenizer. For example, `@Separator(";")` splits properties on semicolons instead of commas. The `COLLECTION` converter passes this separator to the tokenizer before converting individual tokens to the target element type.

### Why doesn't Owner provide a built-in Map converter?

Map structures require explicit knowledge of key-value parsing rules that vary by application—some use colons, others use equals signs, and some require nested delimiters. Because this logic cannot be generically inferred from plain strings, Owner requires you to implement `Converter<Map<K,V>>` and annotate the method with `@ConverterClass` to supply your parsing logic.

### How do I handle nested collections or arrays of maps?

Define methods that return arrays of collections, such as `Map<String,String>[]` or `List<String>[]`. Owner first applies your custom converter (for Maps) or the standard collection converter to each element, then assembles the results into an array. The outer array conversion is handled by the standard array conversion logic in the type-conversion engine.