在Java中返回反转的通用列表类型

5

我正在进行一些有关泛型编程的练习;是否有一种方法可以获取实现了List接口的类并返回其反转版本呢?似乎这应该是可行的,因为至少从字面上来看,这是“泛型编程”的写法。

也许通过原地反转来实现?我也考虑过Collections.reverse(),但它是一个无返回值的方法。

以下是我的尝试和演示:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Arrays;

public class ReverseDemo {

public static <T> List<T> reverse(List<T> list) {
    List<T> reversed = new ArrayList<T>();

    for (int i = list.size() - 1; i >= 0; i--) {
        reversed.add(list.get(i));
      }

    return reversed;
}

public static void main(String[] args) {
    LinkedList<Integer> linkedInt = new LinkedList<Integer>();
    ArrayList<Double> arrayDouble = new ArrayList<Double>();

    for (int k = 0; k < 10; k++) {
        double doubleNum = 10*Math.random();
        int intNum = (int) (10*Math.random());
        linkedInt.add(intNum);
        arrayDouble.add(doubleNum);
    }

    // LinkedList<Integer> demo
    System.out.println(Arrays.toString(linkedInt.toArray()));
    System.out.println(Arrays.toString(reverse(linkedInt).toArray()));
    System.out.println(reverse(linkedInt) instanceof LinkedList<?>);  // false

    // ArrayList<Double> demo
System.out.println(Arrays.toString(arrayDouble.toArray()));        
System.out.println(Arrays.toString(reverse(arrayDouble).toArray()));          
System.out.println(reverse(arrayDouble) instanceof ArrayList<?>);  // true
}
}

顺便说一下,这是我在这里的第一篇帖子,有人知道在保留Eclipse缩进和空格的情况下直接发布代码的最佳方法吗?我已经使用了在此处指定的四个空格方法,但它有点不一致。


1
在你的问题上:在Eclipse中,使用Tab键缩进超过通常的量,然后只需复制粘贴即可。这是我从一开始就一直在做的。 - Marko Topolnik
7个回答

3
Guava库有一个不错的、非破坏性的解决方案。参见Lists.reverse(List)。他们定义了一组ReverseList类,用于包装输入的List。从这里开始,只需要转换所有的调用(虽然“只是”可能有点低估了问题的复杂性)。

2

如果您想保留原始列表,可以尝试使用以下方法:

originalList.getClass().newInstance()

这并不是一个100%正确的解决方案,因为如果原始类没有默认构造函数,则可能会抛出异常。然而,大多数集合都有默认构造函数来创建空实例。


2

请尝试以下操作:

public static <T> List<T> reverse(List<T> list) {

    List<T> reversed=null;
    try {
        reversed = list.getClass().newInstance();
        Collections.reverse(list);
        reversed.addAll(list);

    } catch (InstantiationException | IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    };

     return reversed;
}

如果原始列表没有公共构造函数,例如Arrays.asList(...),则此操作将失败。 - splungebob

1
< p > Collections.reverse() 可能是 void,但这只是因为你应该传入要反转的 List。 < /p >
List<T> myList = ...;
Collections.reverse(myList);

你现在有一个反转的列表。


仅仅,它是具有破坏性的。制作一个非破坏性的方法是一项挑战。 - Marko Topolnik
1
真的,但似乎Op并不在意这个。 - greedybuddha

1
所有Listjava.util实现都是可克隆的,因此您可以使用它,但不幸的是,这需要使用反射。在反射章节中,您还可以使用复制构造函数,所有Java集合都支持它。
不幸的是,没有完全通用的非破坏性反转方法。
另一方面,破坏性反转过于琐碎,没有趣味性。

你也可以通过序列化进行克隆。 - splungebob
深度克隆有什么问题吗?我在原帖中没有看到这样的限制。 - splungebob
非常错误。这不是列表反转操作的预期语义。更不用说,如果由列表、所有成员、所有成员等形成的完整对象图仅包含单个不可序列化实例,则会失败。 - Marko Topolnik
是的,我的有关序列化的评论并没有完全合格(即,对象图的所有成员都必须可序列化),这就是我没有将其发布为解决方案的原因。我只是在评论另一种克隆方式(与反射相比)。您能否解释一下您的评论“这只是列表反转操作的预期语义”,因为我不太同意。OP正在尝试编写此操作,那么未编写的内容如何具有意外的语义? - splungebob
好的,我现在明白了。另一种表达这个期望的方式是,反转是可逆操作 - 很好的解释。 - splungebob
显示剩余4条评论

0

这似乎有效:

import java.util.*;

public class ReverseListDemo
{
  public static void main(String[] args)
  {
    List<String> original = Arrays.asList("A", "B", "C");
    List<String> reversal = reverse(original);

    System.out.println("Original: " + original);
    System.out.println("Reversal: " + reversal);
  }

  public static <T> List<T> reverse(List<T> list)
  {
    T[] objects = (T[]) list.toArray();
    List<T> copy = Arrays.asList(objects);
    Collections.reverse(copy);
    return copy;
  }
}

谢谢。我看到这个特定的代码块是有效的,但当我尝试在我的主方法中使用它时,使用instanceOf()测试返回类型时得到了“false”。这可能有一个“过于学术”的原因,或者我错过了instanceOf()的语义。感谢您的建设性评论。 - yangmillstheory
这里的reverse方法返回一种特定类型的列表(由Arrays.asList()使用的内部类),而不是与输入列表相同的类,正如OP所需的那样。 - newacct
我误解了需求。我以为 OP 想要另一个相同泛型类型的列表。 - splungebob

0

谢谢大家的回复。我写了一个名为reverse2的方法,至少适用于实现类ArrayList和LinkedList。不确定它效率如何。

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Arrays;

// See https://dev59.com/5HLYa4cB1Zd3GeqPaKVw.
public class ReverseDemo {

    public static <T> List<T> reverse1(List<T> list) {
        List<T> reversed = new ArrayList<T>();

        for (int i = list.size() - 1; i > -1; i--) {
            reversed.add(list.get(i));
          }

        return reversed;
    }

    public static <T> List<T> reverse2(List<T> list) {
        int size = list.size();

        ArrayList<T> obArray = new ArrayList<T>();
        obArray.addAll(list);

        ListIterator<T> iter = list.listIterator();  
        for (int i = 0; i < size; i++) {
            iter.next();
            iter.set(obArray.get(size - 1 - i));
        }       

        return list;
    }

    public static void main(String[] args) {
        LinkedList<Integer> linkedInt = new LinkedList<Integer>();
        ArrayList<Double> arrayDouble = new ArrayList<Double>();

        for (int k = 0; k < 10; k++) {
            double doubleNum = 10*Math.random();
            int intNum = (int) (10*Math.random());
            linkedInt.add(intNum);
            arrayDouble.add(doubleNum);
        }

        TextIO.putln("Testing reverse1.");

        // LinkedList<Integer> demo
        System.out.println(Arrays.toString(linkedInt.toArray()));
        System.out.println(Arrays.toString(reverse1(linkedInt).toArray()));
        TextIO.putln("LinkedList structure preserved?");
        System.out.println(reverse1(linkedInt) instanceof LinkedList<?>);

        // ArrayList<Double> demo
        System.out.println(Arrays.toString(arrayDouble.toArray()));
        System.out.println(Arrays.toString(reverse1(arrayDouble).toArray()));
        TextIO.putln("ArrayList structure preserved?");
        System.out.println(reverse1(arrayDouble) instanceof ArrayList<?>);

        TextIO.putln("\nTesting reverse2.");

        // LinkedList<Integer> demo
        System.out.println(Arrays.toString(linkedInt.toArray()));
        System.out.println(Arrays.toString(reverse2(linkedInt).toArray()));
        TextIO.putln("LinkedList structure preserved?");
        System.out.println(reverse2(linkedInt) instanceof LinkedList<?>);

        // ArrayList<Double> demo
        System.out.println(Arrays.toString(arrayDouble.toArray()));
        System.out.println(Arrays.toString(reverse2(arrayDouble).toArray()));
        TextIO.putln("ArrayList structure preserved?");
        System.out.println(reverse2(arrayDouble) instanceof ArrayList<?>);
    }

}

控制台输出:

测试 reverse1。 [8, 0, 1, 9, 3, 4, 3, 7, 6, 3] [3, 6, 7, 3, 4, 3, 9, 1, 0, 8] LinkedList 结构被保留了吗? false [8.301783107294664, 5.434068303620735, 9.095396759542615, 0.41823972682620836, 9.56659902304762, 3.2560723280079085, 4.037362000077436, 9.731919590391389, 0.5243645318825874, 5.9432185528462975] [5.9432185528462975, 0.5243645318825874, 9.731919590391389, 4.037362000077436, 3.2560723280079085, 9.56659902304762, 0.41823972682620836, 9.095396759542615, 5.434068303620735, 8.301783107294664] ArrayList 结构被保留了吗? true

测试reverse2。 [8, 0, 1, 9, 3, 4, 3, 7, 6, 3] [3, 6, 7, 3, 4, 3, 9, 1, 0, 8] 链表结构保持不变? 是 [8.301783107294664,5.434068303620735,9.095396759542615,0.41823972682620836,9.56659902304762,3.2560723280079085,4.037362000077436,9.731919590391389,0.5243645318825874,5.9432185528462975] [5.9432185528462975,0.5243645318825874,9.731919590391389,4.037362000077436,3.2560723280079085,9.56659902304762,0.41823972682620836,9.095396759542615,5.434068303620735,8.301783107294664] 数组列表结构保持不变? 是


1
reverse2()是具有破坏性的(它会改变原始列表对象)。如果你要这样做,为什么不直接使用以下代码:public static List reverse2(List list) { Collections.reverse(list); return list; } - newacct
你是正确的。没有非破坏性或破坏性的要求,但我现在明白我正在重新发明轮子。你知道为什么通用的非破坏性反转如此难以实现吗? - yangmillstheory
因为没有通用的方法来创建另一个相同类型的对象。 - newacct

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接