Java 映射集合元素并保留对源集合的引用

Java 映射集合元素并保留对源集合的引用,java,collections,Java,Collections,我正在寻找一种方法来创建一个集合、列表、集合或映射,其中包含原始集合的转换元素,并反映该集合中的每个修改 例如,如果我有一个来自第三方API的列表,而另一个API需要一个列表。我知道我可以这样变换列表: List<Integer> intList = thirdPartyBean.getIntListProperty(); List<String> stringList = intList.stream().map(Integer::toString) .coll

我正在寻找一种方法来创建一个集合、列表、集合或映射,其中包含原始集合的转换元素,并反映该集合中的每个修改

例如,如果我有一个来自第三方API的
列表
,而另一个API需要一个
列表
。我知道我可以这样变换列表:

List<Integer> intList = thirdPartyBean.getIntListProperty();
List<String> stringList = intList.stream().map(Integer::toString)
    .collect(Collectors.toList());
secondBean.setStringListProperty(stringList);
List<String> stringList = new MappedList<>(intList, Integer::toString, Integer::valueOf);
因此,我正在搜索类似于
List.sublist(from,to)
的内容,其中结果由原始列表“支持”

我正在考虑实现我自己的列表包装器,其使用方式如下:

List<Integer> intList = thirdPartyBean.getIntListProperty();
List<String> stringList = intList.stream().map(Integer::toString)
    .collect(Collectors.toList());
secondBean.setStringListProperty(stringList);
List<String> stringList = new MappedList<>(intList, Integer::toString, Integer::valueOf);
List-stringList=new-MappedList(intList,Integer::toString,Integer::valueOf);
第二个lambda用于反转转换,以支持像
stringList.add(String)
这样的调用


但是在我自己实现它之前,我想知道我是否尝试重新发明轮子-也许这个问题已经有了一个通用的解决方案?

考虑到您的示例,
列表,您需要在第一个列表的顶部创建一个包装器。
现在,如果希望
List
反映对
List
所做的所有运行时更改,您有两个解决方案

  • 不要创建初始的
    列表
    ,使用一个方法或包装器,该方法或包装器将始终从
    列表
    返回转换后的值,这样您就永远不会有一个静态的
    列表

  • 列表
    周围创建一个包装器,该包装器应具有对
    列表
    的引用,并重写add()、addAll()、remove()和removeAll()方法。在覆盖的方法中,更改
    列表的状态


  • 这正是政府所要解决的问题


    您可以围绕
    List
    List
    创建两个包装器,并让第一个包装器观察另一个包装器的状态

    我会将列表包装在另一个带有变压器的
    列表中

    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]
    }
    
    公共类MappedList扩展了AbstractList{
    私人最终名单来源;
    变压器的专用最终功能;
    变压器专用最终功能;
    公共映射列表(列表源、函数from transformer、函数to transformer){
    this.source=源;
    this.fromTransformer=fromTransformer;
    此。toTransformer=toTransformer;
    }
    公共T获取(整数索引){
    从transformer.apply(source.get(index))返回;
    }
    公共T集(整数索引,T元素){
    从transformer.apply(source.set(索引,toTransformer.apply(元素))返回);
    }
    公共整数大小(){
    返回source.size();
    }
    公共空添加(整数索引,T元素){
    源.add(索引,toTransformer.apply(元素));
    }
    公共T删除(整型索引){
    从transformer.apply(source.remove(index))返回;
    }
    }
    专用无效测试(){
    List intList=newarraylist(Arrays.asList(1,2,3));
    List stringList=new MappedList(intList,String::valueOf,Integer::valueOf);
    增加(4);
    stringList.remove(0);
    System.out.println(intList);//Prints[2,3,4]
    System.out.println(stringList);//Prints[2,3,4]
    }
    
    请注意,如果
    source
    可能包含
    null
    ,则来自Transformer的
    需要
    null
    检查输入值


    现在您没有将原始列表转换为另一个列表并与原始列表失去联系,您正在向原始列表添加转换。

    另一个选项是使用JavaFX
    ObservableList
    类,该类可以用一个可观察层包装现有列表,您可以在该层上定义要传播的操作

    下面是一个从字符串列表传播到整数列表的示例:

    List<String> strList = new ArrayList<>();
    List<Integer> intList = new ArrayList<>();
    ObservableList<String> strings = FXCollections.observableList(strList);
    strings.addListener((ListChangeListener<String>) change -> {
        if(change.next()) {
            if (change.wasAdded()) {
                change.getAddedSubList().stream().map(Integer::valueOf).forEach(intList::add);
            } else if (change.wasRemoved()) {
                change.getRemoved().stream().map(Integer::valueOf).forEach(intList::remove);
            }
        }
    });
    
    strList = strings;
    
    strList.add("1");
    strList.add("2");
    strList.add("2");
    System.out.println(intList);
    strList.remove("1");
    System.out.println(intList);
    

    我不知道您使用的是哪个版本的JDK,但是如果您对使用JavaFX库没问题,您可以使用
    observateList
    。您不需要修改现有列表,因为
    ObservableList
    java.util.list
    的包装器。这有一个例子

    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);
        }
    }
    
    import java.util.concurrent.AtomicBoolean;
    导入java.util.function.function;
    导入javafx.collections.FXCollections;
    导入javafx.collections.ObservableList;
    导入javafx.collections.ListChangeListener.Change;
    公共类可观察列表{
    //防止堆栈溢出
    私有静态最终AtomicBoolean wasChanged=新的AtomicBoolean(false);
    公共静态无效更改(更改<?扩展T>c,可观察列表列表,函数转换){
    如果(wasChanged.get()){
    wasChanged.set(false);
    返回;
    }
    wasChanged.set(true);
    while(c.next()){
    如果(c.wasAdded()&&!c.wasareplacement()){
    对于(T str:c.getRemoved())
    list.add(convert.apply(str));
    }否则,如果(c.已替换()){
    for(int i=c.getFrom();i intList=FXCollections.observearraylist();
    addAll(1,2,3,4,5,6,7);
    ObservableListstringList=FXCollections.observableArrayList();
    添加所有(“1”、“2”、“3”、“4”、“5”、“6”、“7”);
    addListener((Change<?extends Integer>c)->Change(c,stringList,num->Integer.toString(num));
    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);
    }
    }
    
    公共静态void main(字符串…参数){
    List intList=observeList.createBase(新的ArrayList(Arrays.asList(1,2,3,4,5));
    List stringList=ObservableList.createBase(intList,String::valueOf);
    stringList.remove(0);
    增加(6);
    System.out.println(String.join(“,stringList));
    System.out.println(intList.stream().map(String::valueOf).collect(collector.joining(“”));
    }
    @抑制警告({“un
    
    public static void main(String... args) {
        List<Integer> intList = ObservableList.createBase(new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)));
        List<String> stringList = ObservableList.createBase(intList, String::valueOf);
    
        stringList.remove(0);
        intList.add(6);
    
        System.out.println(String.join(" ", stringList));
        System.out.println(intList.stream().map(String::valueOf).collect(Collectors.joining(" ")));
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static final class ObservableList<T, E> extends AbstractList<E> {
    
        // original list; only this one could be used to add value
        private final List<T> base;
        // current snapshot; could be used to remove value;
        private final List<E> snapshot;
        private final Map<Function<T, ?>, List> cache;
    
        public static <T, E> List<E> createBase(List<T> base) {
            Objects.requireNonNull(base);
    
            if (base instanceof ObservableList)
                throw new IllegalArgumentException();
    
            return new ObservableList<>(base, null, new HashMap<>());
        }
    
        public static <T, R> List<R> createBase(List<T> obsrv, Function<T, R> func) {
            Objects.requireNonNull(obsrv);
            Objects.requireNonNull(func);
    
            if (!(obsrv instanceof ObservableList))
                throw new IllegalArgumentException();
    
            return new ObservableList<>(((ObservableList<T, R>)obsrv).base, func, ((ObservableList<T, R>)obsrv).cache);
        }
    
        @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
        private ObservableList(List<T> base, Function<T, E> func, Map<Function<T, ?>, List> cache) {
            this.base = base;
            snapshot = func != null ? base.stream().map(func).collect(Collectors.toList()) : (List<E>)base;
            this.cache = cache;
            cache.put(func, snapshot);
        }
    
        @Override
        public E get(int index) {
            return snapshot.get(index);
        }
    
        @Override
        public int size() {
            return base.size();
        }
    
        @Override
        public void add(int index, E element) {
            if (base != snapshot)
                super.add(index, element);
    
            base.add(index, (T)element);
    
            cache.forEach((func, list) -> {
                if (func != null)
                    list.add(index, func.apply((T)element));
            });
        }
    
        @Override
        public E remove(int index) {
            E old = snapshot.remove(index);
    
            for (List<?> back : cache.values())
                if (back != snapshot)
                    back.remove(index);
    
            return old;
        }
    }
            System.out.println(String.join(" ", stringList));
            System.out.println(intList.stream().map(String::valueOf).collect(Collectors.joining(" ")));
        }
    
    
        private static final class ObservableList<E> extends AbstractList<E> {
    
            private final List<List<?>> cache;
            private final List<E> base;
    
            public static <E> List<E> create(List<E> delegate) {
                if (delegate instanceof ObservableList)
                    return new ObservableList<>(((ObservableList<E>)delegate).base, ((ObservableList<E>)delegate).cache);
                return new ObservableList<>(delegate, new ArrayList<>());
            }
    
            public static <T, R> List<R> create(List<T> delegate, Function<T, R> func) {
                List<R> base = delegate.stream().map(func).collect(Collectors.toList());
                List<List<?>> cache = delegate instanceof ObservableList ? ((ObservableList<T>)delegate).cache : new ArrayList<>();
                return new ObservableList<>(base, cache);
            }
    
            @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
            private ObservableList(List<E> base, List<List<?>> cache) {
                this.base = base;
                this.cache = cache;
                cache.add(base);
            }
    
            @Override
            public E get(int index) {
                return base.get(index);
            }
    
            @Override
            public int size() {
                return base.size();
            }
    
            @Override
            public void add(int index, E element) {
                for (List<?> back : cache)
                    back.add(index, element);
            }
    
            @Override
            public E remove(int index) {
                E old = base.remove(index);
    
                for (List<?> back : cache)
                    if (back != base)
                        back.remove(index);
    
                return old;
            }
        }
    
    List list = new ArrayList<Object>();
    
    list.stream().map(String::valueOf).<do_something>.collect(toList())
    
    public static void testInteger(List<Integer> list) {
        list.add(3);
        list.remove(0);
    }
    
    public static void testString(List<String> list) {
        list.add("4");
        list.remove(0);
    }
    
    public static void main(String...args) {
        List list = new ArrayList<Object>(Arrays.asList("1", "2"));
        testInteger(list);
        System.out.println(list.toString()); // will print: [2, 3]  
        testString(list);
        System.out.println(list.toString()); // will print: [3, 4] 
    }
    
    public static void main(String...args) {
        List list = new ArrayList<Object>(Arrays.asList("1", "2"));
        testInteger(list);
        System.out.println(list.toString()); // will print: [2, 3]  
        testString(list);
        System.out.println(list.toString()); // will print: [3, 4] 
        accessData(list); //Will crash
    }
    
    public static void accessData(List<Integer> list) {
        Integer i = list.get(0); //Will work just fine
         i = list.get(1); //Will result in an Class Cast Exception even tho the Method might define it as List<Integer>
    }
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Random;
    import java.util.stream.Collectors;
    
    public class Main {
    
        static List<Integer> intListProperty;
        static List<String> stringList;
    
        public static void main(String... args) throws InterruptedException {
            Main m = new Main();
            m.execute();
        }
    
    
        private void updateAlways(Main main) {
    
            class OneShotTask implements Runnable {
                Main main;
                OneShotTask(Main main) {
                    this.main = main;
                }
                public void run() {
                    while (main.intListProperty == main.getIntListProperty()) {}
    
                    main.intListProperty = getIntListProperty();
                    main.stringList = main.intListProperty.stream().map(s -> String.valueOf(s)).collect(Collectors.toList());
    
                    main.updateAlways(main);
    
                }
            }
            Thread t = new Thread(new OneShotTask(main));
            t.start();
    
        }
    
        public void execute() throws InterruptedException {
    
            System.out.println("Starting monitoring");
    
            stringList = new ArrayList<>();
    
            intListProperty = new ArrayList<>();
            intListProperty.add(1);
            intListProperty.add(2);
            intListProperty.add(3);
    
            updateAlways(this);
    
            while(true) {
                Thread.sleep(1000);
                System.out.println("\nintListProperty: " + intListProperty.toString()); // will print: [1, 2, 3, 4]
                System.out.println("stringList:      " + stringList.toString()); // will print: [2, 3]
            }
    
        }
    
        // simulated
        //thirdPartyBean.getIntListProperty();
        private List<Integer> getIntListProperty() {
    
            long timeInMilis = System.currentTimeMillis();
    
    
            if(timeInMilis % 5000 == 0 && new Random().nextBoolean()) {
                Object[] objects = intListProperty.toArray();
    
                // change memory position
                intListProperty = new ArrayList<>();
                intListProperty = new ArrayList(Arrays.asList(objects));
                intListProperty.add(new Random().nextInt());
            }
    
            return intListProperty;
        }
    
    }