我试图打印出一个列表的所有元素,但它是打印对象的指针,而不是值。

这是我的打印代码…

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

有人能告诉我为什么它不打印元素的值吗?


当前回答

考虑一个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)- 通常使用集合的迭代器,它被设计为快速失败,这意味着如果底层集合在迭代期间被结构修改,它将抛出ConcurrentModificationException。正如在ArrayList的文档中提到的:

结构修改是添加或删除一个或 更多的元素,或者显式地调整支持数组的大小;只是设置 元素的值不是结构修改。

它的意思是数组列表。对于每个设置值都是允许的,没有任何问题。在并发收集的情况下,例如ConcurrentLinkedQueue,迭代器将是弱一致的,这意味着在forEach中传递的操作甚至可以在不抛出ConcurrentModificationExceptionexception的情况下进行结构更改。但是这里的修改可能在迭代中可见,也可能不可见。

第二种方法(Stream.forEach)- 顺序没有定义。虽然它可能不会出现在连续流中,但规范并不能保证它。此外,该行为还必须是不干涉性质的。如doc中所述:

此操作的行为明显是非确定性的。为 平行流管道,此操作不保证 尊重溪流的遭遇顺序,因为这样做会牺牲 并行的好处。

第三种方法(Stream.forEachOrdered)- 该操作将按照流的遇到顺序执行。因此,每当顺序问题时,请使用forEachOrdered。正如文件中提到的:

在相遇中为此流的每个元素执行一个操作 如果流具有定义的遇到顺序,则为流的顺序。

当迭代一个同步集合时,第一种方法将获得集合的锁一次,并在所有对action方法的调用中保持它,但在流的情况下,他们使用集合的spliterator,它不锁,依赖于已经建立的不干扰规则。如果在迭代过程中修改了支持流的集合,则会抛出ConcurrentModificationException异常或可能出现不一致的结果。

第四种方法(Parallel Stream.forEach)- 正如已经提到的,在并行流的情况下,不能保证尊重遇到的顺序。有可能在不同的线程中对不同的元素执行不同的操作,而forEachOrdered则永远不会出现这种情况。

第五种方法(Parallel Stream.forEachOrdered)- forEachOrdered将按照源指定的顺序处理元素,而不管流是顺序的还是并行的。所以在并行流中使用这个是没有意义的。

其他回答

考虑一个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)- 通常使用集合的迭代器,它被设计为快速失败,这意味着如果底层集合在迭代期间被结构修改,它将抛出ConcurrentModificationException。正如在ArrayList的文档中提到的:

结构修改是添加或删除一个或 更多的元素,或者显式地调整支持数组的大小;只是设置 元素的值不是结构修改。

它的意思是数组列表。对于每个设置值都是允许的,没有任何问题。在并发收集的情况下,例如ConcurrentLinkedQueue,迭代器将是弱一致的,这意味着在forEach中传递的操作甚至可以在不抛出ConcurrentModificationExceptionexception的情况下进行结构更改。但是这里的修改可能在迭代中可见,也可能不可见。

第二种方法(Stream.forEach)- 顺序没有定义。虽然它可能不会出现在连续流中,但规范并不能保证它。此外,该行为还必须是不干涉性质的。如doc中所述:

此操作的行为明显是非确定性的。为 平行流管道,此操作不保证 尊重溪流的遭遇顺序,因为这样做会牺牲 并行的好处。

第三种方法(Stream.forEachOrdered)- 该操作将按照流的遇到顺序执行。因此,每当顺序问题时,请使用forEachOrdered。正如文件中提到的:

在相遇中为此流的每个元素执行一个操作 如果流具有定义的遇到顺序,则为流的顺序。

当迭代一个同步集合时,第一种方法将获得集合的锁一次,并在所有对action方法的调用中保持它,但在流的情况下,他们使用集合的spliterator,它不锁,依赖于已经建立的不干扰规则。如果在迭代过程中修改了支持流的集合,则会抛出ConcurrentModificationException异常或可能出现不一致的结果。

第四种方法(Parallel Stream.forEach)- 正如已经提到的,在并行流的情况下,不能保证尊重遇到的顺序。有可能在不同的线程中对不同的元素执行不同的操作,而forEachOrdered则永远不会出现这种情况。

第五种方法(Parallel Stream.forEachOrdered)- forEachOrdered将按照源指定的顺序处理元素,而不管流是顺序的还是并行的。所以在并行流中使用这个是没有意义的。

System.out.println(列表);对我有用。

下面是一个完整的例子:

import java.util.List;    
import java.util.ArrayList;

public class HelloWorld {
     public static void main(String[] args) {
        final List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("World");
        System.out.println(list);
     }
}

它将打印[Hello, World]。

public static void main(String[] args) {
        answer(10,60);

    }
    public static void answer(int m,int k){
        AtomicInteger n = new AtomicInteger(m);
        Stream<Integer> stream = Stream.generate(() -> n.incrementAndGet()).limit(k);
        System.out.println(Arrays.toString(stream.toArray()));
    }

列表中的对象必须实现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中使用时打印的内容,我们在屏幕上看到了一些更好的东西。

从Java 8开始,List继承了一个默认的“forEach”方法,你可以将它与方法引用“System”结合起来使用。输出::println”像这样:

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