[java特性]集合类:Collection,List,Set,Map

类集五大核心接口Collection、List、Set、Map、Iterable,实现了基础的动态长度的数据结构,利用泛型保存相同类型数据避免向下转型,接口的使用需要子类实现

[toc]

类集接口 Java.util.Collection

所有类集的父接口,为单值集合:每次只能操作一个数据对象

public interface Collection<E> extends Iterable<E>
    boolean add (E e)                               向集合保存单个数据
    boolean addAll (Collection<? extends E> c)      向集合追加一组数据
    void clear ()                           清空集合,根节点为空同时GC
    boolean contains (Object o)     查询数据是否存在,需要equals()方法
    boolean remove (Object o)               数据删除,需要equals()方法
    int size ()                 集合中数据数量,至多Integer.MAX_VALUE
    boolean isEmpty ()                                          判空
    Object[] toArray ()                             集合作对象数组返回
    Iterator<E> iterator ()         集合转为iterator接口,用作数据输出

List接口 Java.util.List

List接口允许保存重复元素,允许保存null,含有Collection接口所有方法并作出扩充,List子类包括ArrayList,Vector,LinkedList

public interface List<E> extends Collection<E>
    E get (int index)                                   获取指定索引数据
    E set (int index, E element)                        修改指定索引数据
    ListIterator<E> listIterator ()             返回ListIterator接口对象
    static <E> List<E>  of (E e1, E e2, E e3)   返回一个参数组成的List

List使用

    // 子类实例化
    List<String> list = new ArrayList<>();
    // List<String> list = new LinkedList<>();
    // List<String> list = new Vector<>();
    // 添加数据,与list中存储顺序一致
    list.add("string");
    list.add("string");
    // 获得索引数据
    list.get(0);
    // 判空
    list.isEmpty();
    // 长度
    list.size();
    // 按由前向后顺序删除第一个匹配
    list.remove("string");
    // 输出集合
    System.out.print(list);
    // Iterable接口forEach输出
    list.forEach((str) -> {
        System.out.print(str);
    });
    // forEach方法引用
    list.forEach(System.out::println);

存储引用类型对象

List接口使用remove()、contains()方法时需要覆写equals()方法
String类型已经覆写equals()方法无需重写

    class Custom{
        int num;
        String str;
        public Custom(int num, String str){
            this.num = num;
            this.str = str;
        }

        @Override
        public boolean equals(Object obj) {
            if(obj == this) return true;
            if(obj == null) return false;
            if(!(obj instanceof Custom)) return false;
            Custom c = (Custom)obj;
            return this.num ==c.num  && this.str.equals(c.str);
        }
    }

    public static void main(String[] args){
        List<Custom> list = new ArrayList<>();
        list.add(new Custom(1,"Test1"));
        list.add(new Custom(2,"Test2"));
        list.remove(new Custom(1,"Test1"));
        list.forEach((obj)->{
            System.out.print(obj.num);
            System.out.print(obj.str);
            System.out.println();
        });
    }
2Test2

子类区别

子类 ArrayList Vector LinkedList
内部封装实现 对象数组 对象数组 链表
存储方式 顺序存储 顺序存储 链式存储
内存使用 仅存储数据域 仅存储数据域 需存储指针域
查找时间复杂度 O(1) O(1) O(n)
插入删除性能 移动大量元素性能差 移动大量元素性能差 表尾增改指针删性能好
线程安全性 不安全 安全 不安全

ArrayList子类

继承结构

源码分析[JDK1.8]

ArrayList是对象数组elementData的封装
为避免ArrayList扩充和拷贝导致性能下降,建议预估容量有参构造

无参构造 分配空的对象数组
有参构造 传入参数直接分配对应大小的对象数组
  • 添加扩充流程
  • 源码解析
    private int size;
    transient Object[] elementData;
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    protected transient int modCount = 0;
    private static final int DEFAULT_CAPACITY = 10;
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 
    // 无参构造
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 
    // 有参构造
    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 boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }
 
    // minCapacity = size + 1
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
 
    // 空数组返回10,否则返回size + 1
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
 
    // 容量不够计算尺寸扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // 溢出后结果>0仍可进入grow方法
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
 
    // 扩充容量
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 扩充为原来的1.5倍,溢出从oldCapacity=1796357452开始
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 扩容容量不够(空数组newCapacity=0),扩容后溢出结果>0
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 扩容容量超过MAX_ARRAY_SIZE(包含溢出)调用hugeCapacity传入size+1
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
 
    // size+1溢出抛异常,否则扩充更大值
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

LinkedList子类

继承结构

源码分析[JDK1.8]

LinkedList是链表的封装
无参构造无操作,扩充时只在尾部添加

    // 尾节点
    transient Node<E> last;
 
    // 构造方法
    public LinkedList() { }
 
    // 表尾添加元素
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
 
    // 保存最后一个结点提升插入性能
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

Vector子类

继承结构

与ArrayList完全相同

源码分析[JDK1.8]

Vector同ArrayList类似是对象数组的封装
Vector加入了线程安全机制但会有性能下降
Vector默认扩容策略同ArrayList,改为扩容2倍

    // 无参构造默认初始化容量为10
    public Vector() {
        this(10);
    }
 
    // 指定容量的有参构造方法
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
 
    // 初始化封装对象数组
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
 
    // 添加扩容
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
 
    // 容量增加则扩容
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
 
    // 指定增量按增量扩,否则扩容为旧对象数组长度的两倍
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
 
    // 同ArrayList
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

Set接口 Java.util.Set

Set接口不允许保存重复元素,允许保存null,含有Collection接口所有方法并作出扩充,Set子类包括HashSet,TreeSet,LinkedHashSet
Set接口中无List接口get方法,无法实现指定索引数据获取

Set使用

static <E> Set<E> of (E e1, E e2) // JDK1.9
    // 重复数据报异常
    Set<String> set = Set.of("test","test","hello","world");
    all.forEach(System.out::println);

Set集合通过of方法构造不允许含重复元素,否则报ClassCastException,一般通过子类实例化构造

    Set<String> set = new HashSet<>();
    set.add(null);
    set.add("hello");
    set.add("hello");
    System.out.print(set); // 去重且无序

HashSet子类

数据去重无序保存

继承结构

源码分析[JDK1.8]

HashSet底层封装为HashMap,默认初始化容量为16,到达容量75%时扩容

TreeSet子类

数据去重有序保存

继承结构

自定义类排序

TreeSet保存自定义类对象需要实现Comparable接口,覆写compareTo方法
覆写compareTo方法需要比较类中所有属性,否则属性相同会导致错误去重
添加null元素需要在Comparable / Comparator接口中加入逻辑,否则报空指针

public TreeSet ()                                   默认Comparable接口排序
public TreeSet (Comparator<? super E> comparator)   使用Comparator接口排序
public int compareTo( NumberSubClass referenceName )    返回值<-1=0>1
    class Test implements Comparable<Test> {
        int num;
        String label;

        public Test(int num, String label){
            this.num = num;
            this.label = label;
        }

        @Override
        public String toString() {
            return "num = " + this.num
                +" label = "+ this.label;
        }

        @Override
        public int compareTo(Test o) {
            if (this.num < o.num) {
                return -1;
            } else if (this.num == o.num) {
                return 0;
            } else {
                return 1;
            }
        }
    }

    public static void main(String[] argv) {
        Set<Test> set = new TreeSet<>();
        set.add(new Test(2, "Two"));
        set.add(new Test(3, "Three"));
        set.add(new Test(1, "Four"));
        set.add(new Test(1, "One"));
        System.out.println(set);
    }
[num = 1 label = One, num = 2 label = Two, num = 3 label = Three]

值相等时加入String类型比较,String类已覆写compareTo方法

    @Override
    public int compareTo(Test o) {
        if (this.num < o.num) {
            return -1;
        } else if (this.num > o.num) {
            return 1;
        } else {
            return this.label.compareTo(o.label);
        }
    }
[num = 1 label = Four, num = 1 label = One, num = 2 label = Two, num = 3 label = Three]

集合自定义类去重

TreeSet子类实现SortedSet,NavigabledSet和Comparable接口,通过compareTo方法return 0去重
其他集合子类无法通过Comparable接口去重,类中覆写Object类中的hashCode()和equals()方法去重
先计算和比较hashCode,若相同则使用equals比较内容,避免与集合中所有元素equals一一比较提高性能

    public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }
 
    // 通过hash算法计算出唯一地址
    public static int hashCode(Object a[]) {
        if (a == null)
            return 0;
 
        int result = 1;
 
        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());
 
        return result;
    }
    class Test{
        int num;
        String label;

        public Test(int num, String label){
            this.num = num;
            this.label = label;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Test test = (Test) o;
            return num == test.num && Objects.equals(label, test.label);
        }

        @Override
        public int hashCode() {
            return Objects.hash(num, label);
        }

        @Override
        public String toString() {
            return "num = " + this.num +" label = "+ this.label;
        }
    }

    public static void main(String[] argv) {
        Set<Test> set = new HashSet<>();
        set.add(new Main().new Test(2, "Two"));
        set.add(new Main().new Test(3, "Three"));
        set.add(new Main().new Test(1, "One"));
        set.add(new Main().new Test(1, "One"));
        System.out.println(set);
    }
[num = 1 label = One, num = 2 label = Two, num = 3 label = Three]

集合输出

集合输出四种方式:Iterator、ListIterator、Enumeration、foreach

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

©2018-2024 Howell版权所有 备案号:冀ICP备19000576号