приведение ArrayList.toArray () к ArrayList из универсальных массивов - PullRequest
9 голосов
/ 19 января 2011

Сложный вопрос, на который я близок, чтобы оставить надежду. Я пытаюсь сделать функция, но у меня проблемы с получением ArrayList.toArray() для возврата типа, который я хочу.

Вот минимальный пример, который демонстрирует мою проблему:

public static <T> T[] test(T one, T two) {
    java.util.List<T> list = new ArrayList<T>();
    list.add(one);
    list.add(two);
    return (T[]) list.toArray();
}

Обычно я мог бы использовать форму (T[]) list.toArray(new T[0]), но есть две дополнительные трудности:

  1. Из-за ковариационных правил я не могу типизировать массивы, (T[]) myObjectArray дает ClassCastException
  2. Вы не можете создать новый экземпляр универсального типа, то есть я не могу создать экземпляр new T[]. Также я не могу использовать клон на одном из элементов ArrayList или пытаться получить его класс.

Я пытался получить некоторую информацию, используя следующий вызов:

public static void main(String[] args) {
   System.out.println(test("onestring", "twostrings"));
}

результат имеет вид [Ljava.lang.Object;@3e25a5, свидетельствующий о том, что приведение типа при возврате неэффективно. странно следующее:

public static void main(String[] args) {
    System.out.println(test("onestring", "twostrings").getClass());
}

возвращается с:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at patchLinker.Utilities.main(Utilities.java:286)

Так что я думаю, что он думает, что это метка массива String, но внутренне это массив объектов, и любые попытки получить доступ выявляют это несоответствие.

Если кто-нибудь сможет найти способ обойти это (так как два нормальных обходных пути мне запрещены), я был бы очень признателен.

K.Barad JDK1.6


Редактировать


Спасибо Питеру Лоури за решение начальной проблемы. Теперь вопрос в том, как применить решение к моему менее тривиальному примеру:

public static <T> T[] unTreeArray(T[][] input) {
    java.util.List<T> outList = new ArrayList<T>();
    java.util.List<T> tempList;
    T[] typeVar;
    for (T[] subArray : input) {
        tempList = java.util.Arrays.asList(subArray);
        outList.addAll(tempList);
    }
    if (input.length > 0) {
        typeVar = input[0];
    } else {
        return null;
    }
    return (T[]) outList.toArray((T[]) java.lang.reflect.Array.newInstance(typeVar.getClass(),outList.size()));
}

public static void main(String[] args) {
    String[][] lines = { { "111", "122", "133" }, { "211", "222", "233" } };
    unTreeArray(lines);
}

Результат на данный момент

Exception in thread "main" java.lang.ArrayStoreException
at java.lang.System.arraycopy(Native Method)
at java.util.ArrayList.toArray(Unknown Source)
at patchLinker.Utilities.unTreeArray(Utilities.java:159)
at patchLinker.Utilities.main(Utilities.java:301)

Я все еще делаю некоторые тесты, чтобы увидеть, могу ли я получить более полезную информацию, чем эта, но в данный момент я не уверен, с чего начать. Я добавлю больше информации, которую получу, когда получу.

K.Barad


редактировать 2


Теперь еще немного информации. Я попытался собрать массив вручную и передать элементы поэлементно, поэтому я бы использовал типизацию как T, а не T []. Я нашел интересный результат:

public static <T> T[] unTreeArray(T[][] input) {
    java.util.List<T> outList = new ArrayList<T>();
    java.util.List<T> tempList;
    T[] outArray;
    for (T[] subArray : input) {
        tempList = java.util.Arrays.asList(subArray);
        outList.addAll(tempList);
    }
    if (outList.size() == 0) {
        return null;
    } else {
        outArray = input[0];// use as a class sampler
        outArray =
                (T[]) java.lang.reflect.Array.newInstance(outArray.getClass(), outList.size());
        for (int i = 0; i < outList.size(); i++) {
            System.out.println(i + "  " + outArray.length + "   " + outList.size() + "   "  + outList.get(i));
            outArray[i] = (T) outList.get(i);
        }
        System.out.println(outArray.length);
        return outArray;
    }
}

Я все еще получаю следующий вывод:

0  6   6   111
Exception in thread "main" java.lang.ArrayStoreException: java.lang.String
at patchLinker.Utilities.unTreeArray(Utilities.java:291)
at patchLinker.Utilities.main(Utilities.java:307)

Означает ли это, что вы не можете писать в T[], даже если вам удастся создать его косвенно?

Ответы [ 3 ]

4 голосов
/ 19 января 2011

toArray () всегда возвращает массив объектов.

Чтобы вернуть массив определенного типа, необходимо использовать toArray (Object []).

Чтобы указать правильный тип дляаргумент массива, вам нужно использовать определенный класс или в этом случае использовать отражения.

try

 return (T[]) list.toArray(Array.newInstance(one.getClass(), list.size()));
2 голосов
/ 19 января 2011

Проблема в вашем втором примере кода вызвана тем, что T[] typeVar: Input представляет собой массив с двумя диммерами, поэтому input[0] вернет массив с одним диммером (String [], вместо String, как и ожидалось).

Если вы хотите конвертировать List<T> в T[], вам понадобится T typeVar,

Чтобы исправить это:

public static <T> T[] unTreeArray(T[][] input) {
    java.util.List<T> outList = new ArrayList<T>();
    java.util.List<T> tempList;

    if (input.length == 0) {
        return null;
    }


    T typeVar=null;
    for (T[] subArray : input) {
        if(typeVar==null && subArray.length>0) {
            typeVar=subArray[0];
        }
        tempList = java.util.Arrays.asList(subArray);
        outList.addAll(tempList);
    }

    return outList.toArray((T[]) Array.newInstance(typeVar.getClass(),0));
}

public static void main(String[] args) {
    String[][] lines = { { "111", "122", "133" }, { "211", "222", "233" } };
    String[] result=unTreeArray(lines);

    System.out.println(result);

    System.out.println(result.getClass());

        //added for completion:
    System.out.println( Arrays.toString(result));

}

результирующий вывод:

[Ljava.lang.String; @ 5a405a4 class
[Ljava.lang.String;
[111, 122, 133, 211, 222, 233]

1 голос
/ 19 января 2011

Вы не можете создать массив параметра универсального типа. Простая причина заключается в том, что java обычно стирает информацию общего типа во время выполнения, поэтому среда выполнения не имеет представления о том, что такое T (когда T является параметром универсального типа).

Таким образом, решение состоит в том, чтобы фактически получить информацию о типе во время выполнения. Обычно это делается путем передачи параметра Class<T> type, но в этом случае вы можете избежать этого:

@ ответ Питера выглядит хорошо, и я бы использовал его:).

return (T[]) list.toArray(Array.newInstance(one.getClass(), list.size()));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...