java 将 List<String> 转换为 List<Integer>(或任何扩展 Number 的类)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4108970/
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-10-30 04:47:38  来源:igfitidea点击:

Converting a List<String> to a List<Integer> (or any class that extends Number)

javagenericscollectionsguava

提问by Eliseo Soto

I want to create a very generic utility method to take any Collection and convert it into a Collection of a user selectable class that extends from Number (Long, Double, Float, Integer, etc.)

我想创建一个非常通用的实用方法来获取任何 Collection 并将其转换为从 Number(Long、Double、Float、Integer 等)扩展的用户可选类的 Collection

I came up with this code that uses Google Collections to transform the Collection and to return an Immutable List.

我想出了这段代码,它使用 Google 集合来转换集合并返回一个不可变列表。

import java.util.List;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
/**
     * Takes a {@code List<String>} and transforms it into a list of the
     * specified {@code clazz}.
     * 
     * @param <T>
     * @param stringValues
     *            the list of Strings to be used to create the list of the
     *            specified type
     * @param clazz
     *            must be a subclass of Number. Defines the type of the new List
     * @return
     */
    public static <T extends Number> List<T> toNumberList(List<String> stringValues, final Class<T> clazz) {
        List<T> ids = Lists.transform(stringValues, new Function<String, T>() {
            @SuppressWarnings("unchecked")
            @Override
            public T apply(String from) {
                T retVal = null;
                if (clazz.equals(Integer.class)) {
                    retVal = (T) Integer.valueOf(from);
                } else if (clazz.equals(Long.class)) {
                    retVal = (T) Long.valueOf(from);
                } else if (clazz.equals(Float.class)) {
                    retVal = (T) Float.valueOf(from);
                } else if (clazz.equals(Double.class)) {
                    retVal = (T) Double.valueOf(from);
                } else {
                    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
                }
                return retVal;
            }
        });
        return ImmutableList.copyOf(ids);
    }

It can be used like this:

它可以像这样使用:

// Convert List<String> to List<Long>
List<Long> ids = MiscUtils.toNumberList(productIds, Long.class);

Is my code overkill or how would you simplify it and at the same time keep it generic enough?

我的代码是否矫枉过正,或者您将如何简化它并同时保持足够的通用性?

采纳答案by ColinD

I think the most important aspect of this code is the Functionas opposed to the method itself. I also don't think it makes sense to switch over the subclasses you allow in the Functionbody, as you already know what type of Numberyou want to return at the time the Functionis created. It's also slightly problematic that your method fails if given, say, BigInteger.class.

我认为这段代码最重要的方面是Function方法本身。我也不认为切换您允许在Function正文中使用的子类是没有意义的,因为您NumberFunction创建时已经知道要返回的类型。如果给出,例如BigInteger.class.

Given this, what I would do is create a utility class (let's call it Numbers) and provide methods on it that each return a Function(which can be an enumsingleton) for parsing a Stringas a specific type of Number. That is:

鉴于此,我要做的是创建一个实用程序类(我们称之为Numbers)并在其上提供方法,每个方法都返回 a Function(可以是enum单例),用于将Stringa解析为特定类型的Number. 那是:

public class Numbers {
  public static Function<String, Integer> parseIntegerFunction() { ... }
  public static Function<String, Long> parseLongFunction() { ... }
  ...
}

They could each be implemented something like this:

他们每个人都可以这样实现:

public static Function<String, Integer> parseIntegerFunction() {
  return ParseIntegerFunction.INSTANCE;
}

private enum ParseIntegerFunction implements Function<String, Integer> {
  INSTANCE;

  public Integer apply(String input) {
    return Integer.valueOf(input);
  }

  @Override public String toString() {
    return "ParseIntegerFunction";
  }
}

This can then be used however users want:

然后可以根据用户的需要使用它:

List<String> strings = ...
List<Integer> integers = Lists.transform(strings, Numbers.parseIntegerFunction());

This approach has quite a few advantages over yours:

这种方法比你的方法有很多优点:

  • Doesn't require any switching in the Function... we know what type of number we're creating and just do that. Faster.
  • Is more flexible, in that each Functioncan be used wherever... users aren't forced to use it the way your method does (copying the transformed values into an ImmutableList.
  • You only create the Functions you actually want to allow. If there's no BigIntegerparsing function, users just can't call that, as opposed to having it be completely legal to do that at compile time and then fail at runtime like in your example.
  • 不需要任何切换Function......我们知道我们正在创建什么类型的数字,然后就可以了。快点。
  • 更灵活,因为每个都Function可以在任何地方使用......用户不会被迫像您的方法那样使用它(将转换后的值复制到ImmutableList.
  • 您只需创建Function您实际想要允许的s。如果没有BigInteger解析函数,用户就不能调用它,而不是在编译时这样做是完全合法的,然后像你的例子那样在运行时失败。

As a side note, I'd recommend making the return type of any method that returns an ImmutableListbe ImmutableListrather than List... it provides information that is useful to clients of the method.

作为旁注,我建议为任何返回ImmutableListbe 的方法设置返回类型,ImmutableList而不是List... 它提供对方法的客户端有用的信息。

Edit:

编辑:

If you really needsomething more dynamic (i.e. you want classes that have an instance of some Class<T extends Number>to be able to transform Strings to that Numbertype) you could also add a lookup method like:

如果您真的需要更动态的东西(即您希望具有 some 实例的类Class<T extends Number>能够将Strings 转换为该Number类型),您还可以添加一个查找方法,例如:

public static <T extends Number> Function<String, T> parseFunctionFor(Class<T> type) {
  // lookup the function for the type in an ImmutableMap and return it
}

This has the same problems as your original method, though, if there's a Numbersubclass that you don't provide a Functionfor. It also doesn't seem like there would be many situations where this would be useful.

但是,如果Number您没有提供Functionfor的子类,则这与您的原始方法存在相同的问题。似乎也不会在很多情况下这是有用的。

回答by Eugene Kuleshov

Why don't you implement several transformer functions and pass them to Lists.transform() call?

为什么不实现几个转换器函数并将它们传递给 Lists.transform() 调用?

    public class IntegerTransformer extends Function<String, Integer>() {
        public Integer apply(String from) {
            return Integer.valueOf(from);
        }
    }

So, you could write:

所以,你可以写:

Lists.transform(stringValues, new IntegerTransformer());

If you want to handle types automatically, you can add a transformer factory or a map:

如果你想自动处理类型,你可以添加一个transformer factory或者一个map:

static Map<Class,Function<String,?>> transformers = new HashMap<String,?>();
static {
  transformers.put(Integer.class, new IntegerTransformer());
  transformers.put(Integer.class, new LongTransformer());
  ...
}

public static Function<String,?> get(Class c) {
  Function<String,?> transformer = transformers.get(c);
  if(transformer==null) {
    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
  }
  return transformer;         
}

回答by Mark Peters

Looks good to me.

对我来说看上去很好。

Since you have the Class token, why not avoid the unchecked cast and thus suppress warnings?

既然你有 Class 标记,为什么不避免未经检查的强制转换,从而抑制警告?

retVal = clazz.cast(Double.valueOf(from)); 

回答by Stefan Kendall

You could use reflection, and do something like this:

你可以使用反射,并做这样的事情:

      Method m = clazz.getDeclaredMethod("valueOf", String.class);
      T str = (T) m.invoke(null, from);
      return str;

Untested and possible slow.

未经测试,可能很慢。