如何在Java中打印出一个List里的所有元素?

311

我试图打印出List的所有元素,但是它打印出来的是Object的指针而不是值。

这是我的打印代码...

for(int i=0;i<list.size();i++){
    System.out.println(list.get(i));
} 

请问有谁可以帮我解释一下为什么这段代码没有打印出元素的值。


4
你声明了 List 的什么类型?请展示一下你是如何声明和实例化它的。 - Makoto
2
你需要调用toString方法,这样你就可以得到该类的说明,或者覆盖列表所包含类型的toString方法。 - L7ColWinters
1
这是你告诉它要打印的内容 - 你需要一个不同的toString或其他可读字符串。 - Dave Newton
5
注意,你可以使用更紧凑的语法来完成相同的事情:for (Object obj : list) {System.out.println(obj);} - aroth
我觉得这个问题在这个问题中间间接地得到了回答,而且比本主题提供的答案更加优雅。 - Maximilian Schulz
显示剩余2条评论
23个回答

599

以下代码紧凑,避免了您示例代码中的循环,并提供了漂亮的逗号:

System.out.println(Arrays.toString(list.toArray()));

然而,正如其他人所指出的那样,如果您没有为列表内部的对象实现明智的toString()方法,则会得到您正在观察的对象指针(实际上是哈希码)。无论它们是否在列表中,这都是真实的。


24
仅使用 list.toString() 行吗? - JaskeyLam
33
只是使用“list.toString()”并不会打印出列表中的单独元素,除非它是实现了自定义行为覆盖常规行为(打印类名和哈希码等)的List接口。 - Holly Cummins
1
如果他们使用自定义类并且没有覆盖 toString 方法,你的解决方案也会打印出它的类名和哈希码。 - JaskeyLam
它打印为[value1,value2,value3]。我们能否不带[]打印? - deadend
实际上,@Jaskey是正确的 - 他们的答案更好。Arrays方法对于数组很有帮助,但对于AbstractCollection的子类现在不再需要了。 - Holly Cummins
从Java 1.2开始,每个集合都有一个不错的toString()方法,对吧?参考:https://docs.oracle.com/javase/8/docs/api/java/util/AbstractCollection.html?is-external=true#toString-- - Andrea Bergonzo

150
自Java 8以来,List继承了一个默认的“forEach”方法,您可以将其与方法引用“System.out::println”结合使用,如下所示:
list.forEach(System.out::println);

2
@Katja Hahn,是否可以在println输出中添加或前置一些字符串? - humkins
2
@gumkins,是的,你可以。例如:Arrays.stream(new String[] { "John", "Sansa", "Cersei" }).map(s -> "Hi " + s + "!").forEach(System.out::println); - Tiago Mussi
哇,谢谢你! - Haardik Dharma
对于Java/Android: 调用需要API级别24(当前最小值为21):java.lang.Iterable#forEach - Red M

133

以下是一个打印列表组件的示例:

public class ListExample {

    public static void main(String[] args) {
        List<Model> models = new ArrayList<>();

        // TODO: First create your model and add to models ArrayList, to prevent NullPointerException for trying this example

        // Print the name from the list....
        for(Model model : models) {
            System.out.println(model.getName());
        }

        // Or like this...
        for(int i = 0; i < models.size(); i++) {
            System.out.println(models.get(i).getName());
        }
    }
}

class Model {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

2
Model类的介绍与这个问题有什么关系? - Kalle Richter
4
这只是一种假设,因为我不知道列表中包含哪种类型的对象。 - Crazenezz

59
System.out.println(list);//toString() is easy and good enough for debugging.

toString()方法的使用非常简单,AbstractCollection类已经实现了该方法。AbstractListAbstractCollection的子类,因此不需要使用for循环或toArray()方法。

返回此集合的字符串表示形式。字符串表示形式由集合元素列表组成,按其迭代器返回的顺序排列,在方括号(“[]”)中括起来。相邻元素由字符“, ”(逗号和空格)分隔。元素将通过String.valueOf(Object)转换为字符串。

如果您在列表中使用任何自定义对象,例如Student,则需要重写其toString()方法(重写此方法总是一个好习惯),以获得有意义的输出。

请参见以下示例:

public class TestPrintElements {

    public static void main(String[] args) {

        //Element is String, Integer,or other primitive type
        List<String> sList = new ArrayList<String>();
        sList.add("string1");
        sList.add("string2");
        System.out.println(sList);

        //Element is custom type
        Student st1=new Student(15,"Tom");
        Student st2=new Student(16,"Kate");
        List<Student> stList=new ArrayList<Student>();
        stList.add(st1);
        stList.add(st2);
        System.out.println(stList);
   }
}


public  class Student{
    private int age;
    private String name;

    public Student(int age, String name){
        this.age=age;
        this.name=name;
    }

    @Override
    public String toString(){
        return "student "+name+", age:" +age;
    }
}

输出:

[string1, string2]
[student Tom age:15, student Kate age:16]

2
因为这个答案并不完全正确。它在使用多态性时没有意识到。这是list的实际继承层次结构:List -> Collection -> Iterable -> Object。实际从AbstractCollection继承的类是ArrayList。所以在这个例子中,当调用toString()时,它会找到在AbstractCollection中定义的ArrayList实现。请注意ArrayList的继承层次结构:ArrayList -> AbstractList -> AbstractCollection -> Collection -> Iterable -> Object - fIwJlxSzApHEZIl

44

使用 String.join() 方法,例如:

System.out.print(String.join("\n", list));

25

Java 8的Stream方法...

list.stream().forEach(System.out::println);

1
same answer as Katja Hahn's - Kalle Richter
不,它不是。我的利用了stream()。 - Bradley D
8
@BradleyD,添加另一层方法调用会带来什么收益? - Leon

16

列表中的对象必须实现 toString 方法,才能在屏幕上输出有意义的内容。

下面是一个快速测试,可以看到它们之间的差异:

public class Test {

    public class T1 {
        public Integer x;
    }

    public class T2 {
        public Integer x;

        @Override
        public String toString() {
            return x.toString();
        }
    }

    public void run() {
        T1 t1 = new T1();
        t1.x = 5;
        System.out.println(t1);

        T2 t2 = new T2();
        t2.x = 5;
        System.out.println(t2);

    }

    public static void main(String[] args) {        
        new Test().run();
    }
}

当此代码执行时,输出结果为:

t1 = Test$T1@19821f
t2 = 5

由于T1没有重写toString方法,因此它的实例t1的输出结果并不是非常有用。另一方面,T2重写了toString方法,因此当它在I/O操作中使用时,我们可以控制其输出内容,并在屏幕上看到更好的结果。


15

15
考虑一个 List<String> stringList,可以使用Java 8构造以多种方式进行打印:
stringList.forEach(System.out::println);                            // 1) Iterable.forEach
stringList.stream().forEach(System.out::println);                   // 2) Stream.forEach (order maintained generally but doc does not guarantee)
stringList.stream().forEachOrdered(System.out::println);            // 3) Stream.forEachOrdered (order maintained always)
stringList.parallelStream().forEach(System.out::println);           // 4) Parallel version of Stream.forEach (order not maintained)
stringList.parallelStream().forEachOrdered(System.out::println);    // 5) Parallel version ofStream.forEachOrdered (order maintained always)

这些方法有何不同之处?

第一种方法(Iterable.forEach)- 通常使用集合的迭代器,该迭代器被设计为fail-fast,这意味着如果在迭代期间对基础集合进行了结构修改,则会抛出ConcurrentModificationException。如ArrayListdoc中所述:

结构修改是添加或删除一个或多个元素,或显式调整支持数组大小的任何操作;仅设置元素的值不属于结构修改。

因此,对于ArrayList.forEach,设置值是允许的,没有任何问题。而对于并发集合(例如ConcurrentLinkedQueue),迭代器将是weakly-consistent,这意味着可以在forEach中传递的操作甚至可以进行结构更改,而不会抛出ConcurrentModificationException异常。但是,在这里进行的修改可能会或可能不会在该迭代中可见。

第二种方法 (Stream.forEach) - 顺序是未定义的。虽然对于顺序流可能不会发生,但规范并不保证它。此外,操作需要具有非干扰性质。如doc中所述:

此操作的行为明确是不确定的。对于并行流管道,此操作不保证遵守流的遇到顺序,因为这样做将牺牲并行性的好处。

第三种方法 (Stream.forEachOrdered) - 操作将按照流的遇到顺序执行。因此,每当顺序很重要时,请毫不犹豫地使用 forEachOrdered。如doc中所述:

对该流的每个元素执行操作,如果流已定义了遇到顺序,则按照流的遇到顺序执行。

在迭代同步集合时,第一种方法 会一次获取集合的锁,并在调用操作方法的所有调用中保持该锁,但是在流的情况下,它们使用集合的 spliterator,它不会锁定并依赖于已经建立的非干扰性规则。如果在迭代过程中修改了支持流的集合,则会抛出 ConcurrentModificationException 或可能导致不一致的结果。

第四种方法(并行Stream.forEach)- 如前所述,无法保证并行流按预期的遇到顺序进行。可能会在不同的线程中为不同的元素执行操作,而这在使用forEachOrdered时永远不可能发生。

第五种方法(并行Stream.forEachOrdered)- forEachOrdered将按源指定的顺序处理元素,无论流是顺序还是并行。因此,在并行流中使用它没有意义。


12

我遇到过类似的问题。我的代码:

List<Integer> leaveDatesList = new ArrayList<>();

.....inserted value in list.......

方法1:使用for循环打印列表

for(int i=0;i<leaveDatesList.size();i++){
    System.out.println(leaveDatesList.get(i));
}

方法二:使用forEach和for循环打印列表

for(Integer leave : leaveDatesList){
    System.out.println(leave);
}

第三种方式:在Java 8中打印列表

leaveDatesList.forEach(System.out::println);

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