Java:将一种元素类型的列表转换为另一种类型的列表

时间:2010-03-07 09:47:36

标签: java list element

我正在编写一个适配器框架,我需要将一个对象列表从一个类转换为另一个类。我可以遍历源列表来执行此操作,如

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

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

11 个答案:

答案 0 :(得分:13)

我对此问题的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时会发生转换。

答案 1 :(得分:9)

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

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

    // 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);
    

    这是有用的,你必须每次都使用转换,封装过程。 因此,您可以轻松地创建集合映射器库。

答案 2 :(得分:9)

Java 8方式:

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

假设Wrapper类的构造函数接受String

答案 3 :(得分:2)

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

这样的事情:

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);
        }
    }
}

答案 4 :(得分:1)

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());

非常简单。不是吗?

答案 5 :(得分:1)

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

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()));

答案 6 :(得分:1)

如果您想获取列表中的元素列表,请使用以下代码。此处的对象列表包含属性名称,下面为您提供该列表中的名称列表

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

答案 7 :(得分:0)

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

你也可以在goons-collections的公共集合中使用一些变换器类,但它们都在幕后做同样的事情:)以下是一种方式

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

答案 8 :(得分:0)

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

这是一个将任何迭代器包装到String迭代器的简单示例,使用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();
    }
}

答案 9 :(得分:0)

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

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 :(得分:0)

这是一种动态方法。 (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();
      }

    }

  }