【问题标题】:Converting lists of one element type to a list of another type将一种元素类型的列表转换为另一种类型的列表
【发布时间】:2011-01-24 15:31:09
【问题描述】:

我正在编写一个适配器框架,我需要将对象列表从一个类转换为另一个类。我可以像在

中那样遍历源列表来执行此操作

Java: Best way of converting List<Integer> to List<String>

但是,我想知道在迭代目标列表时是否有办法即时执行此操作,因此我不必遍历列表两次。

【问题讨论】:

    标签: java list element


    【解决方案1】:

    如果您要获取列表中的元素列表,请使用以下代码。这里的对象列表包含属性名称,下面为您获取该列表中的名称列表

    inputList.stream().map(p -> p.getName()).collect(Collectors.toList());
    

    【讨论】:

      【解决方案2】:

      这可能是一个解决方案 --> 使用地图

      List<Employee> employee = Arrays.asList(new Emp(1, 100), new Emp(2, 200), new Emp(3, 300));
      List<Employee> employeS = employee.stream()
                                                   .map(emp -> new Emp(emp.getId(), emp.getSalary * 100))
                                                   .collect(Collectors.toList());
      employeS .stream() .forEach(s -> System.out.println("Id :" + s.getId() + " Salary :" + s.getSalary()));
      

      【讨论】:

        【解决方案3】:

        这是一种即时方法。 (jdk中肯定已经有这样的东西了,就是找不到。)

          package com.gnahraf.util;
        
          import java.util.AbstractList;
          import java.util.List;
          import java.util.Objects;
          import java.util.function.Function;
        
          /**
           * 
           */
          public class Lists {
        
            private Lists() { }
        
        
            public static <U,V> List<V> transform(List<U> source, Function<U, V> mapper) {
              return new ListView<U, V>(source, mapper);
            }
        
            protected static class ListView<U, V> extends AbstractList<V> {
        
              private final List<U> source;
              private final Function<U, V> mapper;
        
              protected ListView(List<U> source, Function<U, V> mapper) {
                this.source = Objects.requireNonNull(source, "source");
                this.mapper = Objects.requireNonNull(mapper, "mapper");
              }
        
              @Override
              public V get(int index) {
                return mapper.apply(source.get(index));
              }
        
              @Override
              public int size() {
                return source.size();
              }
        
            }
        
          }
        

        【讨论】:

          【解决方案4】:

          Java 8 方式:

          List<String> original = ...;
          List<Wrapper> converted = original.stream().map(Wrapper::new).collect(Collectors.toList());
          

          假设Wrapper 类有一个接受String 的构造函数。

          【讨论】:

            【解决方案5】:

            作为迭代器模式的替代方案,您可以使用抽象的通用映射器类,并且只覆盖转换方法:

            1. 为任何数据类型创建通用集合映射器
            2. [可选] 创建在不同数据类型之间转换的方法库(并覆盖该方法)
            3. 使用那个库

            实施:

            // Generic class to transform collections
            public abstract class CollectionTransformer<E, F> {
            
                abstract F transform(E e);
            
                public List<F> transform(List<E> list) {
                    List<F> newList = new ArrayList<F>();
                    for (E e : list) {
                        newList.add(transform(e));
                    }
                    return newList;
                }
            }
            
            // Method that transform Integer to String
            // this override the transform method to specify the transformation
            public static List<String> mapIntegerToStringCollection(List<Integer> list) {
            
                CollectionTransformer transformer = new CollectionTransformer<Integer, String>() {
                    @Override  
                    String transform(Integer e) {
                        return e.toString();
                    }
                };
                return transformer.transform(list);
            }
            
            // Example Usage
            List<Integer> integers = Arrays.asList(1,2);
            List<String> strings = mapIntegerToStringCollection(integers);
            

            这很有用,因为您每次都必须使用转换,封装流程。 因此,您可以非常轻松地制作集合映射器库。

            【讨论】:

              【解决方案6】:

              Lambdaj 允许以非常简单易读的方式做到这一点。例如,假设您有一个 Integer 列表,并且您想将它们转换为相应的 String 表示形式,您可以编写类似的内容;

              List<Integer> ints = asList(1, 2, 3, 4);
              Iterator<String> stringIterator = convertIterator(ints, new Converter<Integer, String> {
                  public String convert(Integer i) { return Integer.toString(i); }
              });
              

              Lambdaj 仅在您对结果进行迭代时应用转换函数。 还有一种更简洁的方式来使用相同的功能。下一个示例假设您有一个具有 name 属性的人员列表,并且您希望将该列表转换为人员姓名的迭代器。

              Iterator<String> namesIterator = convertIterator(persons, on(Person.class).getName());
              

              很简单。不是吗?

              【讨论】:

                【解决方案7】:

                您可以编写一个映射迭代器来装饰现有的迭代器并在其上应用一个函数。在这种情况下,该函数“即时”将对象从一种类型转换为另一种类型。

                类似这样的:

                import java.util.*;
                
                abstract class Transformer<T, U> implements Iterable<U>, Iterator<U> {
                    public abstract U apply(T object);  
                
                    final Iterator<T> source;       
                    Transformer(Iterable<T> source)    { this.source = source.iterator(); }
                
                    @Override public boolean hasNext() { return source.hasNext(); }
                    @Override public U next()          { return apply(source.next()); }
                    @Override public void remove()     { source.remove(); } 
                
                    public Iterator<U> iterator()      { return this; }
                }
                
                public class TransformingIterator { 
                    public static void main(String args[]) {
                        List<String> list = Arrays.asList("1", "2", "3");
                        Iterable<Integer> it = new Transformer<String, Integer>(list) {
                            @Override public Integer apply(String s) {
                                return Integer.parseInt(s);
                            }
                        };
                        for (int i : it) {
                            System.out.println(i);
                        }
                    }
                }
                

                【讨论】:

                • 这看起来很接近我正在寻找的东西。根据类似的回复,即时执行此操作是否会给我带来很多优势?
                • 老实说,我认为 Google Collections 解决方案(Ben Lings 的)可能会更好。他们的设计更加成熟,并且他们的实现更加健壮。坦率地说,我只是在 15 分钟内完成了该代码,并没有考虑太多。
                • 我的意思是,该类实现了Iterator&lt;U&gt; 和Iterable&lt;U&gt;。我不确定这是否符合犹太教规。
                • 确实,对于泛化集合来说是个坏主意。
                【解决方案8】:

                我的answer 这个问题适用于您的情况:

                import com.google.common.collect.Lists;
                import com.google.common.base.Functions
                
                List<Integer> integers = Arrays.asList(1, 2, 3, 4);
                
                List<String> strings = Lists.transform(integers, Functions.toStringFunction());
                

                转换后的列表是原始集合上的一个视图,因此转换发生在目标List 被访问时。

                【讨论】:

                • 如果我们有自定义类来代替 Integer 和 String 怎么办?
                【解决方案9】:

                我认为您要么必须创建自定义列表(实现 List 接口),要么必须创建自定义迭代器。例如:

                ArrayList<String> targetList = new ArrayList<String>();
                ConvertingIterator<String> iterator = new ConvertingIterator<String>(targetList);
                // and here you would have to use a custom List implementation as a source List
                // using the Iterator created above
                

                但我怀疑这种方法会为您节省多少。

                【讨论】:

                  【解决方案10】:

                  好吧,您可以创建自己的迭代器包装类来执行此操作。但我怀疑这样做是否会节省很多。

                  这是一个简单的例子,它将任何迭代器包装到一个字符串迭代器,使用 Object.toString() 来进行映射。

                  public MyIterator implements Iterator<String> {
                  
                      private Iterator<? extends Object> it;
                  
                      public MyIterator(Iterator<? extends Object> it) {
                          this.it = it;
                      }
                  
                      public boolean hasNext() {
                          return it.hasNext();
                      }
                  
                      public String next() {
                          return it.next().toString();
                      }
                  
                      public void remove() {
                          it.remove();
                      }
                  }
                  

                  【讨论】:

                    【解决方案11】:

                    该问题不会遍历列表两次。它只迭代一次,是迄今为止唯一已知的方法。

                    您还可以在 google-collections 的 commons-collections 中使用一些转换器类,但它们在后台都做同样的事情 :) 以下是一种方式

                    CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());
                    

                    【讨论】:

                    • 列表不是迭代(至少)两次;转换完成时在原始列表上一次,然后在迭代目标列表时再次?
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-01-04
                    • 1970-01-01
                    • 2011-03-05
                    • 1970-01-01
                    • 2021-01-03
                    • 2021-11-12
                    • 1970-01-01
                    相关资源
                    最近更新 更多