从一个案例开始:遍历一个集合
外部迭代
最传统的方法是用Iterator,当然还以用for i、增强for循环等等。这一类方法叫做外部迭代,意为显式地进行迭代操作,即集合中的元素访问是由一个处于集合外部的东西来控制的,在这里控制着循环的东西就是迭代器。
我们自己定义一个List,叫ContactList:电话本List
public class ContactList extends ArrayList<Integer>{}
里面存他们的编号,现在我们要都输出,可以这么做
for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) { System.out.println(contactListIterator.next()); }
内部迭代
顾名思义,这种方式的遍历将在集合内部进行,我们不会显式地去控制这个循环。无需关心遍历元素的顺序,我们只需要定义对其中每一个元素进行什么样的操作。注意在这种设定下可能无法直接获取到当前元素的下标。
比如JDK8提供的最新的Collection.forEach(…)方法。
forEach方法源码,一看究竟
@Override public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
可以发现其内部依旧是使用了一个for循环遍历本身,只不过对并发做了一些处理而已。可见外部迭代与内部迭代并没有本质上的区别,两者存在形式上的不同。
性能比较
public static void main(String[] args) { final long count = 100_00000; List<Long> list = new ArrayList<>(); for (long i = 0; i < count; i++) { list.add(i); } //=========传统方式进行外部迭代========= Instant begin = Instant.now(); for (Long i : list) { System.out.print(""); } System.out.println("--------------------------"); Instant end = Instant.now(); System.out.println("传统方式进行外部迭代" + count + "次,耗时(ms):" + Duration.between(begin, end).toMillis()); //=========java8内部迭代,用lambda处理========= begin = Instant.now(); list.stream().forEach(i -> System.out.print("")); System.out.println("--------------------------"); end = Instant.now(); System.out.println("内部迭代forEach" + count + "次,耗时(ms):" + Duration.between(begin, end).toMillis()); //=========java8进行并行流处理后迭代(备注:并行流输出是没有顺序的 比如不再是1234顺序了)========= begin = Instant.now(); list.parallelStream().forEach(i -> System.out.print("")); System.out.println("--------------------------"); end = Instant.now(); System.out.println("内部迭代parallelStream" + count + "次,耗时(ms):" + Duration.between(begin, end).toMillis()); }
这段代码,有兴趣的各位可以去执行,对比一下效率。
结论
java的内部迭代的推出,使得我们具备了一定的流式处理的能力。特别是当数据量大的时候,一定要使用Stream迭代,内部迭代。