使用 List.subList 进行切片操作居然会导致 OOM? private static List data = new ArrayList(); private static void oom() { for (int i = 0; i < 1000; i++) { List rawList = IntStream.rangeClosed(1, 100000).boxed().collect(Collectors.toList()); data.add(rawList.subList(0, 1)); } } 删除子 List 中的元素影响到了原始 List;尝试为原始 List 增加数字 0 之后再遍历子 List,会出现 ConcurrentModificationException。 List list = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList()); List subList = list.subList(1, 4); System.out.println(subList); subList.remove(1); System.out.println(list); list.add(0); try { subList.forEach(System.out::println); } catch (Exception ex) { ex.printStackTrace(); }
既然 SubList 相当于原始 List 的视图,那么避免相互影响的修复方式有两种: 修复后代码输出如下: 一种是,不直接使用 subList 方法返回的 SubList,而是重新使用 new ArrayList,在构 造方法传入 SubList,来构建一个独立的 ArrayList; 另一种是,对于 Java 8 使用 Stream 的 skip 和 limit API 来跳过流中的元素,以及限制 流中元素的个数,同样可以达到 SubList 切片的目的。 //方式一: List subList = new ArrayList(list.subList(1, 4));
//方式二:
List collect = list.stream().skip(1).limit(4).collect(Collectors.toList());
对于数组,随机元素访问的时间复杂度是 O(1),元素插入操作是 O(n); 对于链表,随机元素访问的时间复杂度是 O(n),元素插入操作是 O(1)。 随机 插入操作居然也是 LinkedList 落败,耗时 9.3 秒,ArrayList 只要 1.5 秒:抛开算法层面不谈,由于 CPU 缓存、内存连续性等问题,链表这种数 据结构的实现方式对性能并不友好,即使在它最擅长的场景都不一定可以发挥威力。 int elementCount = 100000; int loopCount = 100000; StopWatch stopWatch = new StopWatch(); stopWatch.start("linkedListGet"); linkedListGet(elementCount, loopCount); stopWatch.stop(); stopWatch.start("arrayListGet"); arrayListGet(elementCount, loopCount); stopWatch.stop(); System.out.println(stopWatch.prettyPrint()); StopWatch stopWatch2 = new StopWatch(); stopWatch2.start("linkedListAdd"); linkedListAdd(elementCount, loopCount); stopWatch2.stop(); stopWatch2.start("arrayListAdd"); arrayListAdd(elementCount, loopCount); stopWatch2.stop(); System.out.println(stopWatch2.prettyPrint());
//LinkedList访问 private static void linkedListGet(int elementCount, int loopCount) { List list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new)); IntStream.rangeClosed(1, loopCount).forEach(i -> list.get(ThreadLocalRandom.current().nextInt(elementCount))); } //ArrayList访问 private static void arrayListGet(int elementCount, int loopCount) { List list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new)); IntStream.rangeClosed(1, loopCount).forEach(i -> list.get(ThreadLocalRandom.current().nextInt(elementCount))); } //LinkedList插入 private static void linkedListAdd(int elementCount, int loopCount) { List list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new)); IntStream.rangeClosed(1, loopCount).forEach(i -> list.add(ThreadLocalRandom.current().nextInt(elementCount))); } //ArrayList插入 private static void arrayListAdd(int elementCount, int loopCount) { List list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new)); IntStream.rangeClosed(1, loopCount).forEach(i -> list.add(ThreadLocalRandom.current().nextInt(elementCount))); }
调用类型是 Integer 的 ArrayList 的 remove 方法删除元素,传入一个 Integer 包装类 的数字和传入一个 int 基本类型的数字,结果一样吗?
List list = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList()); Collections.shuffle(list);
System.out.println(list); list.remove(1); System.out.println(list); list.remove(Integer.valueOf(1)); System.out.println(list);