Java集合详解


1 集合框架与数组的对比及概述

1、集合、数据都是对多个数据进行存储操作的结构,简称Java容器。
(此时的存储,主要指的是内存层面的存储,不涉及持久化的存储(.txt、数据库等))

2、数组在存储多个数据方面的特点

  • 一旦初始化,其长度就确定了
  • 一旦定义好,其元素的类型也就确定了

3、数组存储的缺点

  • 数组初始化后,长度不可修改
  • 数组提供的方法非常有限,对于增删改数据等操作,非常不方便,并且效率不高
  • 数据存储的特点:有序、可重读。对于无序不可重复的需求,不能满足。

2 集合框架接口

1.Collection接口继承树

在这里插入图片描述List:有序、可重复
Set:无序、不可重复
HashSet:线程不安全,可以存储Null值
LinkedHashSet:作为HashSet子类,遍历其内部数据时,可以按照添加的顺序遍历
TreeSet:可以按照添加对象的指定属性进行排序

1.Set

1.Set无序、不可重复(以HashSet为例)
  • Set集合中没有额外的方法,使用的都是Collection里面的方法
  • 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值来添加。
  • 不可重复性:保证添加的元素按照equals()判断时,不能返回true,即相同的元素只能添加一个。
2.Set添加元素的过程(以HashSet为例)

向Set中添加的数据,其所在类一定要重写hashCode()和equals()。重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码。

  • 向HashSet添加a元素,调用元素a所在类的hashCode()方法,计算a的哈希值
  • 此哈希值接着通过某种算法计算出HashSet底层数组的存放位置(索引位置)
    判断数组在此位置上是否有元素
  • 如果此位置上没有其他元素,则a添加成功(情况1)
  • 如果该位置有其他元素(或者是以链表形式存在多个元素),则比较a与b的hash值
    ----如果hash值不相同,则a添加成功(情况2)
    ----如果hash值相同,进而需要调用a所在类的equals()方法
    --------equals()返回false,a添加失败(情况3)
    --------equals()返回true,a添加成功

情况2和情况3:a是与已经存在索引位置上数据以链表的方式存储。(七上八下)
jdk1.7:a放到数组中,指向原来的元素。
jdk1.8:原来的元素在数组中,指向元素a。
在这里插入图片描述

2.TreeSet

  • 向TreeSet中添加的数据,要求是相同类的对象。
  • 比较大小时,要实现implements Comparator接口
    ----自然排序Comparable接口,compareTo()返回0,不再是equals()
    ----定制排序Comparator接口,compare()返回0,不再是equals()

2.Map接口继承树

在这里插入图片描述
HashMap:作为Map的主要实现类,线程不安全,效率高,存储null的key和value。
----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序遍历
TreeMap:保证按照添加的key-value进行排序,考虑key的自然排序或者定制排序,底层使用红黑树
Hashtable:作为古老的实现类,线程安全,效率低,不能存储null的key和value。

2 集合的遍历

1.Iterator迭代器遍历Collection

    @Test
    public void test3() {
        Collection c = new ArrayList<Integer>();
        c.add(1);
        c.add(2);

        Iterator iterator = c.iterator();
        while (iterator.hasNext()){
            // 指针下移
            System.out.println(iterator.next());
            if(iterator.next().equals(10)){
                // 移除元素
                iterator.remove();
            }
        }
    }

3 集合的遍历

1.ArrayList源码

  • JDK1.8之后,不是在构造器中创建长度为10的数组,而是在第一次调用add方法才创建。(jdk7中的ArrayList的对象创建类似于单例的饿汉式,而jdk1.8中的ArrayList的对象创建类似于单例的懒汉式,延迟了数组的创建,节省了内存)
  • 如果此次添加导致底层的elementData数组容量不够,则扩容。
  • 默认情况下,扩容为原来容量的1.5倍,同时将原有数组复制到新的数组中
  • 如果大概知道ArrayList的容量为多少,则使用public ArrayList(int initialCapacity)构造方法,可避免频繁扩容
package com.ll;

import java.util.Arrays;

public class ArrayList<E> {
    // 数组的默认长度是10
    private static final int DEFAULT_CAPACITY = 10;

    // 容器是Object[]类型的数组
    transient Object[] elementData;

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    protected transient int modCount = 0;
    private int size;
    
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public ArrayList() {
    	// 没有创建长度为10的数组, DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    // 如果此次添加导致底层的elementData数组容量不够,则扩容。
    // 默认情况下,扩容为原来容量的1.5倍,同时将原有数组复制到新的数组中
    //如果大概知道ArrayList的容量为多少,则使用public ArrayList(int initialCapacity)构造方法,可避免频繁扩容
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    // 确定内部的容量
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 默认情况下,扩容为原来容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 同时将原有数组复制到新的数组中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }
}

2.LinkedList源码

  • list.add(123)将123封装到Node中,创建了Node对象
public class LinkedList<E> {
    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;
        }
    }
    transient Node<E> first;
    transient Node<E> last;
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<E>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }
}

3.Vector源码

方法用synchronized修饰了

    public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

4.HashMap

1.实现原理

1.jdk7

在实例化以后,底层创建了长度是16的一维数组Entry[] table
map.put(key1,value1)
首先计算key1所在类的hashCode()计算key1哈希值,此哈希值是经过某种算法计算以后,得到Entry数组中的存放位置。
如果此位置上的数据为空,此时key1-value1添加成功

2.jdk8

2.源码

jdk7:数组+链表
jdk8:数组+链表+红黑树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值