javaAPI深入理解(2)Collection,List的设计,ListIterator与Iterator,RandomAccess,List排序、逆序以及其他

17 篇文章 1 订阅
11 篇文章 0 订阅

我们知道List通过Collection继承了Iterable接口。从List到Iterable的继承关系为:
public interface Collection extends Iterable
public interface List extends Collection

1 Iterator模式为Collection提供了遍历和删除

我们看一下Iterable的方法列表

public abstract Iterator<T> iterator(); Iterable
public default Spliterator<T> spliterator(); Iterable
public default void forEach(Consumer<? super T>); Iterable

我们回忆一下Iterator定义的方法
根据迭代器模式的规则,Iterator的核心定义是boolean hasNext()和E next()

public abstract E next(); Iterator
public abstract boolean hasNext(); Iterator
public default void forEachRemaining(Consumer<? super E>); Iterator
public default void remove(); Iterator

我们看一下Collection的方法列表:

public abstract <T> T[] toArray(T[]); Collection
public abstract boolean add(E); Collection
public abstract boolean addAll(Collection<? extends E>); Collection
public abstract boolean contains(Object); Collection
public abstract boolean containsAll(Collection<?>); Collection
public abstract boolean equals(Object); Collection
public abstract boolean isEmpty(); Collection
public abstract boolean remove(Object); Collection
public abstract boolean removeAll(Collection<?>); Collection
public abstract boolean retainAll(Collection<?>); Collection
public abstract int hashCode(); Collection
public abstract int size(); Collection
public abstract Object[] toArray(); Collection
public abstract Iterator<E> iterator(); Collection
public abstract void clear(); Collection
public default boolean removeIf(Predicate<? super E>); Collection
public default Spliterator<E> spliterator(); Collection
public default Stream<E> parallelStream(); Collection
public default Stream<E> stream(); Collection
public default void forEach(Consumer<? super T>); Iterable

可以看到一个集合可以增删清,获得元素组成的数组。还可以获得Stream但是不能根据index获得元素,说明Collection并没有规定顺序,或者更准确的说没有规定随机访问(通过下标访问)。直接继承Collection的接口有List,Set,Queue。(Vector继承List,Stack继承Vector)。Collection提供了Iterator iterator()支持迭代器模式。实现的类可以使用迭代器遍历。

2 Collction的foreach和增强for循环以及普通for loop区别和效率跟内部实现有关。

我们再想一下Collection的子类可以使用增强for循环和foeach循环。其实
对于实现Collection接口的类而言foreach循环就是增强for循环,增强for循环就是迭代器循环。这是编译器处理的。Collection增强for循环编译后跟迭代器的字节码一样这里就不再次多讲了。数组也可以用增强for循环,但那实际上是普通的for loop循环。数组不能用foreach循环,除非转为Stream
这也就意味着继承了List的类可以通过以下两种方式循环:

 for (int i=0, n=list.size(); i < n; i++)
         list.get(i);
for (Iterator i=list.iterator(); i.hasNext(); )
         i.next();

List的两个经典实现ArrayList内部是数组适用于前者(普通的for loop),LinkedList内部实现是双向链表适用于后者(迭代器)。性能差个大约30%左右的样子。
这只是一个情况,可以交给未来的编译器优化,或者javaAPI设计者还有其他的考虑。数组和Collection还可以用StreamAPI的Spliterator来foreach遍历。

3 ListIterator为List提供了在任意位置的前后遍历以及修改元素

List还提供了ListIterator 。这个迭代器是专门继承自Iterator为List设计的。目的是提供光标(Cusor)向前或向后的移动。

public abstract E next(); ListIterator
public abstract E previous(); ListIterator
public abstract boolean hasNext(); ListIterator
public abstract boolean hasPrevious(); ListIterator
public abstract int nextIndex(); ListIterator
public abstract int previousIndex(); ListIterator
public abstract void add(E); ListIterator
public abstract void remove(); ListIterator
public abstract void set(E); ListIterator
public default void forEachRemaining(Consumer<? super E>); Iterator

Iterator是为Collection提供的,核心是next();hasNext();java还为它提供了remove。也就是删除。ListIterator是为List设计的提供了向前向后遍历还提供了修改。我们可以想象List下的链表LinkedList是双向链表,才能方便的提供任意位置的光标向前或向后遍历。
看一下LinkedList的节点定义:

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

果不其然。

3 List的toArray()是新的数组

List和Collection都定义了toArray()和toArray(T[])。我们看看List下的ArrayList和LinkedList有什么不同。
ArrayList的toArray:

    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

LinkedList的toArray:

    public Object[] toArray() {
        Object[] result = new Object[size];
        int i = 0;
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
        return result;
    }

4 RandomAccess为List指明内部结构是否是随机访问。

为了区分一个List是否支持随机访问,定义了RandomAccess 作为标记接口。

public interface RandomAccess {
}

4.1 洗牌

有顺序才有所谓的洗牌,Collections.shuffle(List);的参数为list。洗牌的思路就是如果数组有n个元素,则先随机在0~n-2随机出一个下标,然后arr[rand]跟arr[n-1]互换,然后在0~n-3随机出一个下标。。。

    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void shuffle(List<?> list, Random rnd) {
        int size = list.size();
        if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=size; i>1; i--)
                swap(list, i-1, rnd.nextInt(i));
        } else {
            Object arr[] = list.toArray();

            // Shuffle array
            for (int i=size; i>1; i--)
                swap(arr, i-1, rnd.nextInt(i));

            // Dump array back into list
            // instead of using a raw type here, it's possible to capture
            // the wildcard but it will require a call to a supplementary
            // private method
            ListIterator it = list.listIterator();
            for (int i=0; i<arr.length; i++) {
                it.next();
                it.set(arr[i]);
            }
        }
    }
    public static void swap(List<?> list, int i, int j) {
        final List l = list;
        l.set(i, l.set(j, l.get(i)));
    }

4.2 List排序,依赖Arrays.sort(),未区分RandomAccess

有顺序次才能排序。
java8的List已经有sort()方法,Collections.sort(list);也是调用List.sort(),java7的Collections.sort()核心和java8的区别不大。都是依赖于Arrays.sort(obj[]);有兴趣的可以看下Arrays的设计里面包含了很多排序和搜索算法。不过如果对于非常多的数据的排序,不推荐内存排序来解决,可以考虑有顺序的数据结构比如跳跃表,

    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

4.3 List的反转,链表(语言无关)反转的思路

有顺序才有所谓的反转,标记为RandomAccess的ArrayList使用Collections.swap(list, i, j);,最终使用set(index,Object)来反转。而链表(不管是不是双向链表)的思路是遍历,然后每个节点的指向下一个指针全都换成指向前一个节点即可。在这里我们看java思路是同时向中间遍历头节点和尾节点,然后互换。与继承RandomAccess的ArrayList不同的是它移动的是ListIterator的光标而ArrayList移动的是下标index

    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void reverse(List<?> list) {
        int size = list.size();
        if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
                swap(list, i, j);
        } else {
            // instead of using a raw type here, it's possible to capture
            // the wildcard but it will require a call to a supplementary
            // private method
            ListIterator fwd = list.listIterator();
            ListIterator rev = list.listIterator(size);
            for (int i=0, mid=list.size()>>1; i<mid; i++) {
                Object tmp = fwd.next();
                fwd.set(rev.previous());
                rev.set(tmp);
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值