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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2395816/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 06:59:27  来源:igfitidea点击:

Java: Converting lists of one element type to a list of another type

javalistelement

提问by

I'm writing an adapter framework where I need to convert a list of objects from one class to another. I can iterate through the source list to do this as in

我正在编写一个适配器框架,我需要将对象列表从一个类转换为另一个类。我可以遍历源列表来做到这一点

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

Java:将 List<Integer> 转换为 List<String> 的最佳方式

However, I'm wondering if there is a way to do this on the fly when the target list is being iterated, so I don't have to iterate through the list twice.

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

回答by Kannan Ekanath

That question does not iterate through the list twice. It just iterates once and by far is the only known method.

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

Also you could use some transformer classes in commons-collections of google-collections but they all do the same thing under the hood :) the following being one way

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

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

回答by Stephen C

Well, you could create your own iterator wrapper class to do this. But I doubt that you would save much by doing this.

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

Here's a simple example that wraps any iterator to a String iterator, using Object.toString() to do the mapping.

这是一个简单的示例,它使用 Object.toString() 进行映射,将任何迭代器包装为 String 迭代器。

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

回答by polygenelubricants

You can write a mapping iterator that decorates an existing iterator and applies a function on it. In this case, the function transforms the objects from one type to another "on-the-fly".

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

Something like this:

像这样的东西:

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

回答by Tedil

I think you would either have to create a custom List (implementing the List interface) or a custom Iterator. For example:

我认为您必须创建一个自定义 List(实现 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

But I doubt that this approach would save you much.

但我怀疑这种方法是否能为您节省很多。

回答by Ben Lings

My answerto that question applies to your case:

回答这个问题适用于您的情况:

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

The transformed list is a view on the original collection, so the transformation happens when the destination Listis accessed.

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

回答by Mario Fusco

Lambdajallows to do that in a very simple and readable way. For example, supposing you have a list of Integer and you want to convert them in the corresponding String representation you could write something like that;

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 applies the conversion function only while you're iterating on the result. There is also a more concise way to use the same feature. The next example works supposing that you have a list of persons with a name property and you want to convert that list in an iterator of person's names.

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

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

Pretty easy. Isn't it?

挺容易。不是吗?

回答by rhodan

As an alternative to the iterator pattern, you can use a abstract generic mapper class, and only override the transform method:

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

  1. create a generic collection mapper for any data type
  2. [optional] create a library of methods that transform between different data types (and override the method)
  3. use that library
  1. 为任何数据类型创建通用集合映射器
  2. [可选] 创建一个在不同数据类型之间转换的方法库(并覆盖该方法)
  3. 使用那个库

the implementation:

实施:

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

This would be useful is you have to use transformations every time, encapsulating the process. So you can make a library of collection mappers very easy.

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

回答by Alexey Malev

Java 8 way:

Java 8 方式:

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

assuming Wrapperclass has a constructor accepting a String.

假设Wrapper类有一个接受String.

回答by Babak Farhang

Here's an on-the-fly approach. (There must be something already like this in the jdk; I just can't find it.)

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

    }

  }