原始数组的“通用”解决方案?

时间:2011-08-12 14:33:36

标签: java arrays refactoring primitive-types

我有用于处理原始数组输入的类:char []的CharArrayExtractor,byte []的ByteArrayExtractor,int []的IntegerArrayExtractor,...

public void CharArrayExtractor {

    public List<Record> extract(char[] source) {
        List<Record> records = new ArrayList<Record>();
        int recordStartFlagPos = -1;
        int recordEndFlagPos = -1;
        for (int i = 0; i < source.length; i++) {
            if (source[i] == RECORD_START_FLAG) {
                recordStartFlagPos = i;
            } else if (source[i] == RECORD_END_FLAG) {
                recordEndFlagPos = i;
            }
            if (recordStartFlagPos != -1 && recordEndFlagPos != -1) {
                Record newRecord = makeRecord(source, recordStartFlagPos,
                        recordEndFlagPos);
                records.add(newRecord);
                recordStartFlagPos = -1;
                recordEngFlagPos = -1;
            }
        }
    }
}

public void ByteArrayExtractor {

    public List<Record> extract(byte[] source) {
        // filter and extract data from the array.
    }
}

public void IntegerArrayExtractor {

    public List<Record> extract(int[] source) {
        // filter and extract data from the array.
    }
}

这里的问题是提取数据的算法是相同的,只有输入的类型是不同的。每次算法更改时,我都必须更改所有提取器类。

有没有办法让提取器类更“泛型”?

最好的问候。

编辑:到目前为止,似乎每个建议都是使用自动装箱来归档通用。但是阵列的元素数量通常很大,所以我避免使用自动装箱。

我添加了更具体的数据提取方式。希望它能澄清一些事情。

11 个答案:

答案 0 :(得分:3)

新主意

或者另一种方法是包装原始数组并使用您用于算法的方法覆盖它们。

public PrimitiveArrayWrapper {
    private byte[] byteArray = null;
    private int[] intArray = null;
    ...

    public PrimitiveArrayWrapper(byte[] byteArray) {
        this.byteArray = byteArray;
    }

    // other constructors

    public String extractFoo1(String pattern) {
        if(byteArray != null) {
          // do action on byteArray
        } else if(....) 
        ...
    }
}

public class AlgorithmExtractor {
    public List<Record> do(PrimitiveArrayWrapper wrapper) {
        String  s= wrapper.extractFoo1("abcd");
        ...
    }
}

这主要取决于你有很多方法要调用,你需要覆盖。但至少你不能在如何访问原始数组的方式上编辑算法。此外,您还可以在包装器中使用不同的对象。

旧观念

使用泛型或者我也想到的是有三种方法将原始类型转换为值类型。

public void Extractor {
    public List<Record> extract(byte[] data) {
        InternalExtractor<Byte> ie = new InternalExtractor<Byte>();
        return ie.internalExtract(ArrayUtils.toObject(data));
    }

    public List<Record> extract(int[] data) {
        ...
    }
}

public void InternalExtractor<T> {
    private List<Record> internalExtract(T[] data) {
        // do the extraction
    }
}

ArrayUtils是来自Apache的公共资源的助手类。

答案 1 :(得分:2)

interface Source
    int length();
    int get(int index);

extract(final byte[] source)
    extract( new Source(){
        int length(){ return source.length; }
        int get(int i){ return source[i]; }
    } );

// common algorithm
extract(Source source)
    for(int i=0; i<source.lenth(); i++)
        int data = source.get(i);
        ...

答案 2 :(得分:2)

我不确定你的过滤器是如何工作的,因为它不知道数组包含的类型。

使用反射你可以做你想做的事,但是你会失去编译时类型的安全性。

java.lang.reflect.Array类提供了在不知道其类型的情况下操作数组的函数。

Array.get()函数将返回所请求的数组索引处的值,如果是原语,则将其包装在相应的Object类型中。缺点是您必须更改方法签名以接受Object而不是特定的数组类型,这意味着编译器无法再为您检查输入参数。

您的代码将变为:

public class ArrayExtractor {

    public List<Record> extract(Object array) {
        // filter and extract data from the array.
        int length = Array.getLength(array);
        for (int index = 0; index < length; index++) {
            Object value = Array.get(array, index);

            // somehow filter using value here
        }
    }
}

就我个人而言,我宁愿使用反射来保证类型安全,即使它更加冗长。

答案 3 :(得分:0)

取决于你想要达到的目标。但也许你可以使用原始包装器呢?然后你可以编写泛型Extractor<? extends Number>(这里Number是所有原始包装器扩展的抽象类)。

答案 4 :(得分:0)

不是传递每种类型,而是传递类型的类,如下所示:

    public List<Record> extract(Class srcClass) {
        if (int[].class.equals(srcClass) {

           // filter and extract data from the int[] array.
        }
        else if (..) // do same for other types
    }

答案 5 :(得分:0)

public void Extractor<T> {

    public List<Record> extract(T[] source) {
        // filter and extract data from the array.
    }
}

http://download.oracle.com/javase/tutorial/extra/generics/methods.html

答案 6 :(得分:0)

你可以这样做。

public class ArrayExtractor<T>
{
    public List<T> extract (T[] source)
    {
        // filter and extract data from the array.
    }
}

您将拥有一个通用的Extractor类,您的实现将是相同的。

答案 7 :(得分:0)

我认为可以创建这样的方法:

public List<Record> extract(List<Number> source) {
    // filter and extract data from the array.
}

并使用Arrays.asList(yourPrimaryArrayType),使其兼容。


在我的测试和 Sean Patrick Floyd 的评论之后,你可以通过创建一些辅助方法来实现这一点,将原始数组转换为列表:

public static void main(String[] args)
{
    int[] i = {1,2,3};
    System.out.println(extract(asPrimitiveList(i)));
}

public static List<Object> extract(List<Number> source) {
    List<Object> l = new ArrayList<Object>();
    l.add(0);
    for (Number n : source)
    {
        // I know this line is rubbish :D
        l.set(0, ((Number) l.get(0)).doubleValue() + n.doubleValue());
    }
    return l;
}

private static List<Number> asPrimitiveList(int[] ia)
{
    List<Number> l = new ArrayList<Number>(ia.length);
    for (int i = 0; i < ia.length; ++i)
    {
        l.add(ia[i]);
    }
    return l;
}

private static List<Number> asPrimitiveList(byte[] ia)
{
    List<Number> l = new ArrayList<Number>(ia.length);
    for (int i = 0; i < ia.length; ++i)
    {
        l.add(ia[i]);
    }
    return l;
}

private static List<Number> asPrimitiveList(char[] ia)
{
    List<Number> l = new ArrayList<Number>(ia.length);
    for (int i = 0; i < ia.length; ++i)
    {
        l.add(ia[i]);
    }
    return l;
}

答案 8 :(得分:0)

由于原始类型的源代码,您不能使用Java泛型,最好的办法是尝试使用一些java反射API来分析传入的源并自行调用提取器。

答案 9 :(得分:0)

不,永远不可能。

例如,请查看ArrayUtils.copyOf(byte[] original, int newLength)的文档。并且存在用于保留基元的其他方法。这是你想要的一种相同的行为(字面意思)。如果有可能类似的代码应该存在于其他地方。

此外,我们可以讨论更多关于通用的工作原理,但我认为这将是另一个问题。

答案 10 :(得分:-1)

是的,您应该能够使用泛型:

    interface Extractor<T, R> {
        public List<R> extract(T source);
    }

    class BaseExtractor<T> implements Extractor<T, Record>
    {
        public List<Record> extract(T source)
        {
            //do your thing
        }
    }

在这里,你必须假设T是一个原始数组,因为你不能在泛型定义中使用原语。

或者,您可以使用包装器对象并以这种方式执行:

    interface Extractor<T, R> {
        public List<R> extract(T[] source);
    }

    class BaseExtractor<T> implements Extractor<T, Record>
    {
        public List<Record> extract(T[] source)
        {
            //do your thing
        }
    }

在这种情况下,您的通用T可以是ByteInteger等。