Java容器类库 -- 上

1.Iterable,Iterator接口

在这里插入图片描述

  • forEach():用于支持forEach语句,即所有实现了该接口的类,都可以使用forEach遍历
  • iterator(): 返回一个迭代器,用于遍历
    在这里插入图片描述
  • hasNext():确认在 迭代器中是否还有下一个接口
  • next():返回下一个元素
  • remove():移出当前元素
  • Note
  • 当你使用迭代器遍历集合时,若试图进行删除操作(如下所示),必须使用迭代器的remove() 方法。当你使用集合的 remove() 方法时,会产生ConcurrentModificationException ,这是因为集合不允许被不同的线程修改,那么系统又是如何辨别修改集合的操作不是来自于迭代器的 remove() 方法呢? 我们可以提前来看一下AbstractListiterator() 方法的实现。我们观察AbstraList源码时,可以发现一个成员变量modCount,在AbstractListCURD操作时,这个成员都会自加一次,但是在生成迭代器以后,迭代器中的expectedModCount会记录当时的modCount值,并且在每次next()remove() 操作前都会通过 checkForComodification() 来检查expectedModCount 是否等于 modCount ,如果不等于这说明使用了在另外线程中对集合做了修改,则会报错,但是这种方法有一个弊端,因为如果你在集合外只对该集合做一次修改,并且修改完以后就不再使用迭代器,则不会报错,如下所示。

@Test
    public void test01() {
        ArrayList<Integer> list = new ArrayList<>();
        int count = 0;
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        Iterator<Integer> itr = list.iterator();
        while (itr.hasNext()) {
            list.remove(itr.next()); // 错误
            itr.next();
            itr.remove(); // 正确
        }
		while (itr.hasNext()) {   //该方法成功,但是感觉并没有什么ruan用
            if (count == 2) {
                list.remove(itr.next());
                break;
            }
            count++;
        }

    }

//iteraotr():在AbstractList中的实现方法
private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

2.Collection

在这里插入图片描述

  • Collection集合继承了Iterable接口,因此Collection的子实现类都是可以迭代的

3.List

在这里插入图片描述

  • 可以看出在List中,增加了关于index的方法
  • listIterator() : 这个方法和 iterator() 的区别是,前者可以双向遍历,其他都一致

4.AbstractList

在这里插入图片描述

  • indexOf(Object) : 查找对应元素的下标,在遍历元素时,采用了迭代器的方法遍历
  • Note:若在不实现RandomAccess接口的情况下,for循环遍历的效率比迭代器慢很多
public int indexOf(Object o) {
        ListIterator<E> it = listIterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return it.previousIndex();
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return it.previousIndex();
        }
        return -1;
    }
  • subList(int,int):取原List中的一部分作为子集合,与原集合共享存储位置,也就是不论是修改子集合还是原集合,另一个都会被修改。
  • class RandomAccessSubList:实现了RandomAccess的集合,即随机存取,通过查看源代码,发现实现RandomAccess接口的List集合采用一般的for循环遍历,而未实现这接口则采用迭代器。关于RandomAccess的作用请见DriveMan

5.ArrayList

该类实现了RandomAccess接口,所以支持下标访问。

  • 构造函数:
    • ArrayList(int initialCapacity) :根据给定的容量构造
    • ArrayList():构造一个默认容量为10的ArrayList集合
    • ArrayList(Collection<? extends E> c):通过一个Collection 集合构造一个ArrayList 集合
  • ensureCapacity(int minCapacity):用于初始化ArrayList的底层数组,当minCapacity > elementDate.length * 1.5 时,将底层数组扩容为minCapacity,当小于时,则扩容为1.5被,[elementData为底层数组]
  • trimToSize():将ArrayList的大小改成实际拥有元素的长度,可以和ensureCapacity()配合使用,当初始化完成后,由于1.5倍的扩容策略,可能会产生许多null值,因此这个时候调用此方法可以一定程度上优化程序。顺带一提,底层数组的扩容或者缩容多是调用Arrays.copyOf 或者System.arraycopy方法
  • toArray():返回一个Object数组
  • toArray(T[] a):返回一个T类型的数组,建议输入一个空数组,这里只是需要数组的a类型,若输入有元素的数组,元素会被覆盖
  • clone():返回一个深拷贝ArrayList
public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
    //测试
    @Test
    public void test() {
        ArrayList<Integer> list = new ArrayList<>();
        Integer[] s = {1, 2, 4, 5, 7, 5, 5, 1, 2, 3 ,5 , 7};
        for (int i = 0; i < 8; i++) {
            list.add(1);
        }
        List<Integer> sublist = list.subList(0, 6);
        list.set(1, 2);
        s = list.toArray(s);
        System.out.println(Arrays.toString(s));

    }
    //Result:[1, 2, 1, 1, 1, 1, 1, 1, null, 3, 5, 7]
  • removeIf(Predicate<?>):通过实现了接口**Predicate**的类来选择要移除的元素,建议在这里配合使用匿名内部类来进行选择性剔除,这个用法和**Comparator<?>**非常相似。
  • replaceAll():待填坑

6. LinkedList

LinkedList并没有实现RandomAccess接口,故不能实现随机访问。LinkedList实现了Deque接口,所以它是一个双向链表

  • 构造函数
    • LinkedList():创建一个空链表
    • LinkedList(Collection):创建一个链表内部元素由给定集合内的元素组成
  • descendingIterator():返回一个逆序的迭代器
  • 应用:
    • 作为队列:
      • 单向队列:
        • offer():向队列中添加元素
        • peek():查看队列头元素
        • poll():弹出队列头元素
      • 双端队列
        • offerFirst()/offerLast(): 向队列头部/尾部加入元素
        • peekFirst()/peekLast(): 查看队列头部/尾部的第一个元素
        • pollFirst()/pollLast(): 抛出队列头部/尾部的第一个元素
    • 栈:
      • push():入栈
      • pop():出栈
		//单向队列
	    Queue<String> q = new LinkedList<>();
        //向队列尾部加入元素,只有顺序没有标号
        //所以Queue没有和index有关的方法
        q.offer("Tom");
        q.offer("Lucy");
        //从头部获取元素
        String s1 = q.peek();  //查看队列头的元素
        String s2 = q.poll(); //取出队列头的元素
        //双向队列
        Deque<String> dq = new LinkedList<>();
        //从头加和从尾部加
        dq.offerFirst("Tom1");
        dq.offerFirst("Tom2");
        dq.offerLast("Lucy1");
        dq.offerLast("Lucy2");
        System.out.println(dq);
        //从头取元素和从尾部取元素
        dq.peekFirst();
        dq.peekLast();
        //栈
        Deque<String> stack = new LinkedList<>();
        //入栈
        stack.push("Tom");
        stack.push("Tom1");
        stack.push("Ranly");
        //出栈
        stack.pop();
        stack.pop();
        stack.pop();
        System.out.println(stack);

7.ArrayList,LinkedList的效率问题

访问效率:ArrayList > LinkedList
插入删除效率: LinkedList > ArrayList
综合考虑,我们在开发中基本使用ArrayList为主。

8.Set,AbstractSet

在这里插入图片描述

  • hashCode():通过将容器中所有元素的哈希值相加得出哈希值

9.HashSet

HashSet的初始大小为16,默认的负载因子为0.75(用于扩容)

  • 构造函数:HashSet的构造函数都是通过构造一个HashMap来完成,Mapkey部分放入Set的元素,value部分放入Object对象[源码中的对象为PRESENT],默认为null
    • public HashSet():创建一个空的HashSet
    • public HashSet(Collection<? extends E> c):创建一个由指定集合内的元素构成的HashSetSet的初始大小为Math.max((int) (c.size()/.75f) + 1, 16)
    • public HashSet(int initialCapacity, float loadFactor):指定初始容量和负载因子
    • public HashSet(int initialCapacity):指定初始容量
    • default HashSet(int initialCapacity, float loadFactor, boolean dummy)dummy可以忽略

Set从实现上来说可以说是Map的的key部分,阅读源码后,HashSet大多方法的实现都是通过Map的方法完成

10.Map

在这里插入图片描述

Map并不继承Collection接口

  • compute(k, BiFunction<? super K, ? super V, ? extends V> ): V:JDK1.8新增的方法,将key值设置成由原来的keyvalue映射出来的值,如果新valuenull,则删除原来的键值对,返回新value
default V compute(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue = get(key);

        V newValue = remappingFunction.apply(key, oldValue);  //这里使用的BiFunction
        if (newValue == null) {
            // delete mapping
            if (oldValue != null || containsKey(key)) {
                // something to remove
                remove(key);
                return null;
            } else {
                // nothing to do. Leave things as they were.
                return null;
            }
        } else {
            // add or replace old mapping
            put(key, newValue);
            return newValue;
        }
    }
  • computeIfAbsent(K key,Function<? super K, ? extends V> mappingFunction):V:如果给定的key值不存在,则添加由给定key值映射出来的value值,如果新的valuenull,则不操作。如果存在对应的value值,返回value值,如果不存在则返回新的value值。
default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) { //函数值只有在key值不存在的情况下才会计算出来,一定程度上优化了性能
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }
  • computeIfPresent(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction):V:如果key对应的键值对存在,则有函数计算出新的value值,如果value值为null,则删除原来的键值对,如果不为null,则取代原来的value,并返回;如果key对应的键值对不存在,则返回null
default V computeIfPresent(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue;
        if ((oldValue = get(key)) != null) {
            V newValue = remappingFunction.apply(key, oldValue);
            if (newValue != null) {
                put(key, newValue);
                return newValue;
            } else {
                remove(key);
                return null;
            }
        } else {
            return null;
        }
    }
  • putIfAbsent(K key, V value):V:如果key对应的键值对不存在,则添加进去,并返回新的value值;如果存在则返回旧的value值。
	default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }
  • replaceAll(BiFunction<? super K, ? super V, ? extends V> function):void:通过BiFunction函数计算出新的value值,对map中存在的键值对进行修改,用新的value值,代替旧的value值。
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }
  • merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction):V:如果key值对应的键值对存在,则用函数计算出新的value代替,如果新的value值为null,则删除这个键值对,如果原来的减值对不存在,则使用给定的value值代替。
  • interface Entry<K,V>:键值对
    • comparingByKey(Comparator<? super K> cmp)
    • comparingByKey(Comparator<? super K> cmp)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值