0
点赞
收藏
分享

微信扫一扫

java基础 -- java集合(一)

萨摩斯加士奇 2022-03-11 阅读 138

集合

Java 集合类是一种容器,可容纳其他数据,用于存储数量不等多个对象,还可用于保存具有映射关系的关联数组(数据对)
整体感受下,
在这里插入图片描述
按照存储数据格式的不同(单列还是双列),简单的可以分为两大类Collection和Map

Collection

集合就像容器装东西,往进添加或者往出拿,对应的就是添加或者删除,集合类就为这些功能提供了对应的方法。
Collection是List、Set 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
在这里插入图片描述
JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现
Collection中定义的常用抽象方法:

  1. 添加
    add(Object obj)
    addAll(Collection coll)
  2. 获取有效元素的个数
    int size()
  3. 清空集合
    void clear()
  4. 是否是空集合
    boolean isEmpty()
  5. 是否包含某个元素
    boolean contains(Object obj)
  6. 删除
    boolean remove 只会删除找到的第一个元素
  7. 集合是否相等
    boolean equals(Object obj)
  8. 获取集合对象的哈希值
    hashCode()
  9. 遍历
    iterator():返回迭代器对象,用于集合遍历

List

  • 由于数组存储数据有局限性,用来替代数组
  • 元素有序、且可重复,每个对应一个整数型的序号,可根据序号存取容器中的元素
  • 主要实现类:ArrayList、LinkedList和Vector

常用方法:增删改查

  1. void add(int index, Object ele): 在index位置插入ele元素
  2. Object remove(int index): 移除指定index位置的元素,并返回此元素
  3. Object set(int index, Object ele): 设置指定index位置的元素为ele
  4. 2.Object get(int index): 获取指定index位置的元素ele

ArrayList

List的主要实现类,本质上,ArrayList对象引用指向一个‘’变长数组‘’
实现对ArrayList的增删改查:

 public static void main(String[] args) {
//        注意泛型,和迭代器或遍历语句前后保持一致
        List<String> list = new ArrayList();
//        添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("z");

//        输出列表形式
        System.out.println("toString形式:");
        System.out.println(list);

//        for each
        System.out.println("增强for循环:");
        for (String val : list) {
            System.out.println(val);
        }

//      for循环
//      注意:容器的大小是size(),不是length()
        System.out.println("for循环: ");
        for (int i = 0; i < list.size(); i++) {
            System.out.println("waiting...");
        }

//        删除元素
        list.remove("b");
//        下标从0开始
        list.remove(1);
//        修改元素
        list.set(0, "aka hh");

//        使用迭代器循环
//        容器自带iterator()方法,List实现了Collection接口,可以直接 . 出来
        System.out.println("iterator迭代器: ");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

    }

运行结果:
在这里插入图片描述

ArrayList扩容机制:

  1. java8以后,ArrayList类似懒汉式,一开始,创建一个长度为0的数组,当添加第一个元素时再创建一个容量为10的数组,10为其默认初始容量
  /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;
  1. 扩容操作,主要靠下面语句来执行,
    扩容默认为原数组扩容1.5倍(1+0.5),向右移位1
int newCapacity = oldCapacity + (oldCapacity >> 1);

按照默认值扩容后,进行判断,看新数组的长度是否满足要求,如果满足要求,将原数组的数据拷贝到新的数组,完成扩容操作。

/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }

实现扩容的本质
开辟更大的数组空间,将原数组中的数据复制到新的数组中

LinkedList

底层采用链表的方式实现,由于链表地天然属性,对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高

 LinkedList<String> list = new LinkedList<>();

将上面的ArrayList 创建为LinkedList,其余语句保持不变,同样可得到下面运行结果:
在这里插入图片描述

Vector

Vector是线程安全的
再使用List容器时,最好把ArrayList作为默认选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用

List小结

三者的异同:

  1. Vector线程安全,其余两种非线程安全

  2. ArrayList和LinkedList:
    ArrayList实现了基于动态数组的数据结构,对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针
    LinkedList基于链表的数据结构,对于**新增和删除操作*,LinkedList比较占优势,因为ArrayList要移动数据

  3. ArrayList和Vector
    二者几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此,开销就比ArrayList要大,访问慢Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍

Set

Set的概念类似于箱子,东西可以扔入的比较“随意”,没有一个固定的位置,因此,无法通过索引查找,这就要求存入的元素不能重复
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

HashSet

Set接口的主要实现类,没有重复元素,向HashSet中添加数据时,HashSet 根据对象的 hashcode 值来存储该对象,同时也能够根据 hashcode 值来快速定位到该元素。表面无序,实则有序,其顺序来自hashcode
特点:

  • 不能保证元素的排列顺序
  • HashSet 不是线程安全
  • 集合元素可以是 null

对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。
判断两个元素相等hashCode() 相等 && equals() 相等。

HashSet实现及使用方式:

        HashSet<String> list = new HashSet<>();
//        添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");

//        输出列表形式
        System.out.println("toString形式:");
        System.out.println(list);

//        for each
        System.out.println("增强for循环:");
        for (String val : list) {
            System.out.println(val);
        }
        
//        删除元素
        list.remove("b");
        
//        使用迭代器循环
        System.out.println("iterator迭代器: ");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

由于Set中的元素没有下标的概念,因此,不能使用下标访问元素,只能使用上述iterator或for each 来访问集合元素
在这里插入图片描述
相应的,删除元素的方式也只能根据内容来删除特定的元素
在这里插入图片描述
向HashSet中添加元素步骤:

  1. HashSet 会调用该对象的 hashCode() 方法得到 hashCode 值,然后根据 hashCode 值,通过散列函数决定该对象在 HashSet 底层数组中的存储位置。(散列函数会与底层数组的长度相计算得到在数组中的下标)
  2. 如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
    hashcode()值相等,equals()结果也为true的话,则两个元素相等
  3. 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
    图片来自尚硅谷课件
    HashSet扩容方式:
     * @param c the collection whose elements are to be placed into this set
     * @throws NullPointerException if the specified collection is null
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

LinkedHashSet

是HashSet的子类,根据元素的hashCode值确定元素存储位置,且使用双向链表维护存储的顺序,同样地,不允许元素重复

TreeSet

  • 底层使用红黑树存储数据
  • 确保数据处于排序状态
    ps:树形结构属于数据结构中的内容,目前了解的不深,后续再做补充
举报

相关推荐

0 条评论