Map collection elements and keep reference to source collection

I don't know what version of the JDK you are using, but if you are okay with using the JavaFX library you can use ObservableList. You do not need to modify an existing list as ObservableList is a wrapper for java.util.List. Look at extractor in FXCollection for complex Objects. This article has an example of it.

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ListChangeListener.Change;

public class ObservableBiList{
    //prevent stackoverflow
    private static final AtomicBoolean wasChanged = new AtomicBoolean( false);

    public static <T, R> void change( Change< ? extends T> c, ObservableList< R> list, Function< T, R> convert) {
        if( wasChanged.get()){
            wasChanged.set( false);
            return;
        }
        wasChanged.set( true);
        while( c.next()){
            if( c.wasAdded() && !c.wasReplaced()){
                for( T str : c.getRemoved())
                    list.add( convert.apply( str));
            }else if( c.wasReplaced()){
                for( int i=c.getFrom();i<c.getTo();i++)
                    list.set( i,convert.apply( c.getList().get( i)));
            }else if( c.wasRemoved()){
                for( T str : c.getRemoved())
                    list.remove( convert.apply( str));
            }
        }
        System.out.printf( "Added: %s, Replaced: %s, Removed: %s, Updated: %s, Permutated: %s%n",
                c.wasAdded(), c.wasReplaced(), c.wasRemoved(), c.wasUpdated(), c.wasPermutated());
    }

    public static void main( String[] args){

        ObservableList< Integer> intList = FXCollections.observableArrayList();
        intList.addAll( 1, 2, 3, 4, 5, 6, 7);
        ObservableList< String> stringList = FXCollections.observableArrayList();
        stringList.addAll( "1", "2", "3", "4", "5", "6", "7");

        intList.addListener( ( Change< ? extends Integer> c) -> change( c, stringList, num->Integer.toString( num)));
        stringList.addListener( ( Change< ? extends String> c) -> change( c, intList, str->Integer.valueOf( str)));

        intList.set( 1, 22);
        stringList.set( 3, "33");

        System.out.println( intList);
        System.out.println( stringList);
    }
}

This is exactly the kind of problems that the Observer Pattern solves.

You can create two wrappers, around List<String> and List<Integer> and let first wrapper observe the state of the other one.


I would wrap the list in another List with transformers attached.

public class MappedList<S, T> extends AbstractList<T> {
    private final List<S> source;
    private final Function<S, T> fromTransformer;
    private final Function<T, S> toTransformer;

    public MappedList(List<S> source, Function<S, T> fromTransformer, Function<T, S> toTransformer) {
        this.source = source;
        this.fromTransformer = fromTransformer;
        this.toTransformer = toTransformer;
    }

    public T get(int index) {
        return fromTransformer.apply(source.get(index));
    }

    public T set(int index, T element) {
        return fromTransformer.apply(source.set(index, toTransformer.apply(element)));
    }

    public int size() {
        return source.size();
    }

    public void add(int index, T element) {
        source.add(index, toTransformer.apply(element));
    }

    public T remove(int index) {
        return fromTransformer.apply(source.remove(index));
    }

}

private void test() {
    List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3));
    List<String> stringList = new MappedList<>(intList, String::valueOf, Integer::valueOf);
    intList.add(4);
    stringList.remove(0);
    System.out.println(intList); // Prints [2, 3, 4]
    System.out.println(stringList); // Prints [2, 3, 4]
}

Note that the fromTransformer needs null checking for the input value, if source may contain null.

Now you are not transforming the original list into another one and losing contact with the original, you are adding a transformation to the original list.