Java 集合框架之Collection,一文解决

 

JDK提供了一些特殊的类,这些类可以存储任意类型的对象,并且长度可变,在Java中这些被称为集合。按照存储结构可以分为两大类,单列集合Collection和双列集合

常用集合类如下

 

 

Collection

Collection为单列集合类的跟接口,用于存储一些列符合某种规则的元素。有三个重要的子接口,分别是ListSet以及Queue

  • List特点是有序、可重复;Set特点是元素无序且不可重复。
  • List接口的主要实现类有ArrayListLinkListSet接口的主要实现类有HashSetTreeSet

 

方法声明

描述

boolean add(Object o)

向集合添加一个元素

boolean addAll(Collection c)

将指定的Collection中所有元素添加到该集合中

void clear

删除该集合中的所有元素

boolean remove(Object o)

删除该集合中的指定元素

boolean removeALL(Collection c)

删除该集合中所有元素

boolean isEmpty()

判断集合是否为空

boolean contains(Object o)

判断集合是否包含某个元素

boolean containsAll(Collection c)

判断该集合是否包含某个集合中的所有元素

Iterator iterator()

返回在该集合的元素上进行迭代的迭代器,用于遍历该集合的所有元素

int size()

获取该集合的元素个数

 

List

List集合特点:线性存储,有序,可重复的;并且List集合的每个元素都有其对应的顺序索引,即支持索引,索引从0开始。

list不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法。

void add(int index, Object element)

将元素element插入到List集合的index处

void addAll(int index, Collection c)

将集合c所包含的所有元素插入到List集合的index处

Object get(int index)

返回集合索引index处的元素

Object remove(int index)

删除index索引处的元素

Object set(int index, Object element)

将索引index处的元素替换为element对象,并将替换后的元素返回

int indexOf(Object o)

返回对象o在List集合中出现的位置索引

int lastIndexOf(Object o)

返回对象o在List集合中最后一次出现的位置索引

List subList(int fromIndex, int toIndex)

返回从索引fromIndex(包括)到toIndex(不包括)处所有元素组成的子集合

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Demo01 {
    public static void main(String[] args) {
        List list = new ArrayList();//这里定义时没有指定存储类型如List<String> list  = new ArrayList<>();默认可以存储任意类型,如果表明String,那么就只能存储String
        list.add("xml");
        list.add(123);
        list.add('a');
        System.out.println(list);

        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }

        for (Object obj : list){
            System.out.println(obj);
        }

        System.out.println(list.toArray());
    }
}

ArrayList

ArrayListList接口的一个实现类,也是程序中一种最常见的集合。

  • ArrayList可以存放null值并且可以存放多个
  • ArrayList底层使用数组实现的
    • ArrayList扩容机制:
    • ArrayList中维护了一个Object类型的数组elementData
    • 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容为10;如果需要再次扩容,则扩容大小为elementData的1.5倍
    • 如果使用指定大小的构造器,则初始容量为指定的大小,再次扩容则为elementData的1.5倍
    • https://blog.csdn.net/weixin_42621338/article/details/82080167?spm=1001.2014.3001.5502
  • ArrayList是线程不安全的,不过执行效率高。多线程下,不建议使用该集合。

 

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Demo01 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("xml");
        list.add(123);
        list.add('a');
        list.add("xml");
        System.out.println(list);

        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }

        for (Object obj : list){
            System.out.println(obj);
        }

        System.out.println(list.toArray());
    }
}

LinkedList

LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度较慢。

另外,它还提供了List接口没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用(Java中使用的双向链表)

  • LinkedList底层实现了双向链表和双端队列的特点
    • LinkedList底层维护了一个双向链表
    • LinkedList中维护了两个属性first和last,分别指向首节点和尾节点
    • 每个节点里面维护了prev、next、item三个属性;prev:指向前一个、通过next指向后一个节点,最终实现双向链表
    • LinkedList中元素的添加或删除,不是通过数组实现的,效率相对较高
  • 可以任意添加元素,元素可重复、可为null
  • 线程不是安全的,没有实现同步

方法声明

描述

void add(int index, E element)

指定位置插入指定元素

void addFirst(Object o)

将指定元素插入列表开头

void addLast(Object o)

将指定元素插入列表结尾

Object getFirst()

返回列表的第一个元素

Object getLast()

返回列表的最后一个元素

Object removeFirst()

移除并且返回列表的第一个元素

Object removeLast()

移除并且返回列表的最后一个元素

import java.util.*;

public class Demo01 {
    public static void main(String[] args) {
        LinkedList link = new LinkedList();
        link.add("xml");
        link.add(123);
        link.add('y');
        System.out.println(link);

        link.addFirst("xml");
        link.addLast("ywj");
        System.out.println(link);

        link.remove("xml");//如果有重复元素,只能移除第一个出现的
        System.out.println(link);

        link.remove(3);//移除指定索引的元素
        System.out.println(link);

        Object obj = link.removeFirst();
        System.out.println(obj + " " + link);//移除开头的元素
    }
}

ArrayList和LinkedList比较

 

底层结构

增删效率

改查的效率

ArrayList

可变数组

较低,通过数组扩容

较高,不需要改变数组

LinkedList

双向链表

较高,通过链表追加

较低,需要通过node一个个遍历找到后操作

如何选择?

  • 如果改查操作较多,选择ArrayList
  • 如果增删操作较多,选择LinkedList
  • 一般来说在程序中大多数都是查询,因此大部分情况下会选择ArrayList
  • 不适合用于多线程,LinkedList和ArrayList的线程不是安全的

 

Vector(不推荐使用)

VectorArrayList一样也是通过数组实现的,不过不同的是它支持线程的同步,即在某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要的花费很高,因此访问它比访问ArrayList慢。

vector特点

  • 有序的,可以存储重复值和null值
  • 底层是数组实现的,线程安全。结构和ArrayList非常相似,同样是一个线性的动态可扩容数组
  • 初始容量是10,没有设置扩容增量的情况下以自身的2倍容量扩容;可以设置容量增量,初始容量和扩容两可以通过构造函数public Vetor(int initialCapacity, int capacityIncrement)进行初始化

查看Vector源码,和ArrayList几乎一模一样,区别就是Vector的方法使用了synchronized锁实现了线程安全

    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    
    public synchronized E remove(int index) {
        modCount++;
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        E oldValue = elementData(index);

        int numMoved = elementCount - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--elementCount] = null; // Let gc do its work

        return oldValue;
    }

那为什么很少见到使用Vector呢?

  • 因为Vector虽然线程安全,但是每个可能出现线程安全的方法上加上了synchronized关键字,所以效率低。但是其实并没有很好的解决线程安全问题,比如下面这段代码,在判断是否包含某元素后会释放锁,在不包含的情况下,执行add之前,锁很有可能会被抢占
if (!vector.contains(element)) 
    vector.add(element); 
}
  • Vector空间满了以后扩容是一倍,而ArrayList仅是一半。如果数据太多可能会导致内存分配失败
  • 只能在尾部进行插入和删除,效率较低
  • 现在已经有了更好的替代品CopyOnWriteArrayList

 

Set

Set是无序且值不重复的Collection。无序依赖于对象是否相等的判断,对象相等的本质是对象hashcode值判断的,如果想让两个不同的对象视为相等的,就必须覆盖Object的hashcode和equals方法。

 

HashSet

HashSet的哈希表边存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List不相同),而是按照哈希值来存的所以取数据也是按照哈希值取。

元素的哈希值是通过元素的hashcode方法来获取,HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法,如果equals结果为true,HashSet就视为一个元素。如果equals为false就不是同一个元素。HashSet通过hashCode值来确定元素在内存中的位置。因此一个hashCode位置上可以存放多个元素。

import java.util.*;

public class Demo01 {
    public static void main(String[] args) {
        Set set = new HashSet();
        set.add("xml");
        set.add('0');
        set.add("xml");
        set.add(1);
        set.add(null);
        set.add(2);

        Iterator iterator  = set.iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
}

 

TreeSet

TreeSet是使用二叉树的原理对新add()的对象按照指定的顺序排序(升序、降序),每增加一个对象都会进行排序,将对象插入到二叉树指定的位置。

Integer和String对象都可以进行默认的TreeSet排序,而自定义的对象是不可以的,自己定义的类必须实现Comparable接口,并且覆盖写对应的compareTo()函数,才可以正常使用。在覆盖写compare()函数时,要返回相应的值才能使TreeSet按照一定的规则来排序,比较此对象和指定对象的顺序,如果该对象小于、等于或者大于指定对象,则返回负整数、零或正整数。

不过treeset存储的对象必须是同类型的对象,而且存储的类型必须实现了Comparable接口。如果没有实现该接口,那么add进入集合时进行元素大小比较的时候,调用comparableTo方法时会报错ClassCastException异常

import java.util.*;

public class Demo01 {
    public static void main(String[] args) {
        Set set = new TreeSet();
        set.add("xml");
        set.add("ywj");
        set.add("xml");
        //set.add(null); 这里报错java.lang.NullPointerException,是不允许null值的
        set.add("test");

        Iterator iterator  = set.iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
}

 

Iterator

Iterator主要用于迭代访问Collection中的元素,因此Iterator对象也被称为迭代器。案例如上面写的demo。

 
posted @   cccrush  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示