十、Java集合

十、集合(java.util包)

    Collection和Map是集合框架的根接口,List、Set、Queue继承Collection接口。

    1、数组和集合的区别

        数组不是面向对象的,存在明显的缺陷,集合弥补了数组的缺点,比数组更灵活更实用,而且不同的集合框架类可适用不同场合。

        (1)、数组能存放基本数据类型和对象,而集合类存放的都是对象的引用,而非对象本身。

        (2)、数组容量固定无法动态改变,集合类容量动态改变。

        (3)、数组无法判断其中实际存有多少元素,length只告诉了数组的容量,而集合的size()可以确切知道元素的个数。

        (4)、集合有多种实现方式和不同适用场合,不像数组仅采用顺序表方式。

        (5)、集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性即可实现各种复杂操作,大大提高了软件的开发效率。

    2、List、Set、Map区别

        (1)、List:值可重复;有序集合(插入的顺序==输出的顺序);值元素允许为null。

        (2)、Set:值不可重复,重复元素会覆盖掉;Set代表无序集合,HashSet是无序集合,而TreeSet、LinkedHashSet、EnumSet因为结构原因是有序集合;值元素允许为null。

        (3)、Map:值可重复;Map代表无序集合,哈希表内部的排列是无序的。HashMap、Hashtable是无序集合,而TreeMap、LinkedHashMap因为结构原因是有序集合,允许key/value为null,Hashtable不允许。

    3、List

public interface List<E> extends Collection<E> {
    //添加对象元素var1到位置size++
    boolean add(E var1);
    //添加对象元素element到位置index
    void add(int index, E element);
    //在size++位置后添加容器collection中所有的元素
    boolean addAll(Collection<? extends E> var1);
    //在var1位置后添加容器collection中所有的元素
    boolean addAll(int var1, Collection<? extends E> var2);
    //取出下标为var1位置的元素
    E get(int var1);
    //查找对象元素var1在List中第一次出现的位置
    int indexOf(Object var1);
    //查找对象元素var1在List中最后一次出现的位置
    int lastIndexOf(Object var1);
    //删除下标为index位置上的元素
    E remove(int index);
    //返回一个ListIterator跌代器
    ListIterator<E> listIterator();
    //返回一个ListIterator跌代器,开始位置为var1
    ListIterator<E> listIterator(int var1);
    //返回一个子列表List,其元素存放为从var1到var2之间的所有元素
    List<E> subList(int var1, int var2);
}

        (1)、ArrayList:非线程安全,底层通过数组实现,其初始化容量为10,随着元素的增加而动态扩容。具有数组的查询速度快的优点以及增删速度慢的缺点。多线程环境下会报并发修改异常java.util.ConcurrentModificationException

        (2)、LinkedList:非线程安全,底层通过双向链表来实现。双向链表节点对应的类Node的实例,Node中包含成员变量:prev,next,item。其中,prev是该节点的上一个节点,next是该节点的下一个节点,item是该节点所包含的值。

            ①、特性

                a、分配内存空间不是必须是连续的。

                b、插入、删除操作很快,只要修改前后指针就可以,时间复杂度为O(1)。

                c、访问比较慢,必须得从第一个元素开始遍历,时间复杂度为O(n)。

                d、双向链表可用来实现队列和栈。

            ②、常用方法

                a、boolean add(E e):在链表尾部添加一个元素,如果成功,返回true,否则返回false。

                b、void addFirst(E e):在链表头部插入一个元素。

                c、void addLast(E e):在链表尾部添加一个元素。

                d、void add(int index, E element):在链表指定位置插入一个元素。

                e、E remove():移除链表头部第一个元素,如果成功,返回true,否则返回false。

                f、E removeFirst():移除链表头部第一个元素。

                g、E removeLast():移除链表尾部最后一个元素。

                h、boolean remove(Object o):移除链表中指定元素。

                i、E remove(int index):移除链表中指定位置的元素。

        (3)、Vector:线程安全,实现List接口,底层通过数组实现,其初始化容量为10,可以设置扩容时容量增长大小。效率低。Vector里的add等方法都加了synchronized锁,可以保证一致性,但是并发性急剧下降。

        (4)、CopyOnWriteArrayList

            CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行copy,复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,添加完元素之后,再讲原容器的引用指向新的容器。这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

        (5)、解决List非线程安全问题

            ①、List<String> list = new Vector<>();

            ②、List<String> list = Collections.synchronizedList(new ArryList<>());

                a、Collection是接口,是List接口的父类。

                b、Collections是工具类提供了synchronized的一系列方法synchronizedList()、synchronizedMap()、synchronizedSet()等。

            ③、List<String> list = new CopyOnWriteArrayList<>();

    4、Set

public interface Set<E> extends Collection<E> {
    //如果set包含指定元素,返回true
    boolean contains(Object o);
    //返回set中元素的迭代器
    Iterator<E> iterator();
    //返回包含set中所有元素的数组
    Object[] toArray();
    //返回包含set中所有元素的数组,返回数组的运行时类型是指定数组的运行时类型
    <T> T[] toArray(T[] a);
    //如果set中不存在指定元素,则添加
    boolean add(E e);
    //如果set中存在指定元素,则从set中删除
    boolean remove(Object o);
    //如果set包含指定集合,则从set中删除指定集合的所有元素
    boolean removeAll(Collection<?> c);
    //如果set包含指定集合的所有元素,返回true。如果指定集合也是一个set,只有是当前set的子集时,方法返回true
    boolean containsAll(Collection<?> c); 
}

        (1)、HashSet:非线程安全,其初始化容量为16,负载因子为0.75。实现了Set接口,底层依靠HashMap来实现。如果两个元素通过equals为true,并且两个元素的hashCode相等,则这两个元素相等。如果要重写保存在HashSet中的对象的equals方法,也要重写hashCode方法。“键”是要保存的对象,“值”则是一个对象常量。这样可以确保所需要存储的信息都是“键”。而“键”在Map中是不能重复的,这就保证了存入Set中的所有的元素都不重复。

            利用Set元素唯一的特性,可以快速对一个集合进行去重操作,避免使用List的contains方法进行遍历、对比、去重操作。

        (2)、LinkedHashSet:非线程安全,有序集合,继承HashSet,底层依靠LinkedHashMap来实现。结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。

        (3)、TreeSet:非线程安全,有序集合,底层依靠TreeMap来实现,本质上是一个红黑树,元素唯一且已经排好序。唯一性同样需要重写hashCode()和equals()方法,二叉树结构保证了元素的有序性。排序分两种类型,一种是自然排序,另一种是定制排序。

        (4)、EnumSet:非线程安全,有序集合,集合元素必须是枚举类型,其顺序就是Enum类内元素定义的顺序。是所有Set元素中性能最好的,但是它只能保存Enum类型的元素。

    5、Queue

public interface Queue<E> extends Collection<E> {...}
public interface BlockingQueue<E> extends Queue<E> {...}

        (1)、BlockingQueue阻塞队列种类

            ①、ArrayBlockingQueue:由数组结构组成的有界阻塞队列

            ②、LinkedBlockingQueue:由链表结构组成的有界(大小默认值为Integer.MAX_VALUE)阻塞队列

            ③、SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列,产生一个元素,消费一个元素。

        (2)、BlockingQueue方法

            ①、boolean add(E e);

                在不违反容量限制的情况下,可立即将指定元素插入此队列,成功返回true,当无可用空间时候,返回IllegalStateException异常。

            ②、boolean remove(Object o);

                可以将制定元素移除队列,如果队列为空则返回NoSuchElementException异常

            ③、boolean offer(E e);

                在不违反容量限制的情况下,可立即将指定元素插入此队列,成功返回true,当无可用空间时候,返回false。

            ④、void put(E e) throws InterruptedException;

                直接在队列中插入元素,当无可用空间时候,阻塞等待。

            ⑤、E take() throws InterruptedException;

                获取并移除队列头部的元素,无元素时候阻塞等待。

            ⑥、E poll(long timeout, TimeUnit unit)    throws InterruptedException; 

                获取并移除队列头部的元素,无元素时候阻塞等待指定时间。

            ⑦、boolean offer(E e, long timeout, TimeUnit unit)    throws InterruptedException;

                将给定元素在给定的时间内设置到队列中,如果设置成功返回true, 否则返回false。

    6、Stack

public class Stack<E> extends Vector<E> {
    //入栈,把元素压入堆栈顶部
    E push(E item);
    //出栈,移除堆栈顶部的元素,并返回该值
    synchronized E pop();
    //查看堆栈顶部的元素,但不从堆栈中移除它。
    synchronized E peek();
    //测试堆栈是否为空
    boolean empty();
    //返回对象在堆栈中的位置,以1为基数
    synchronized int search(Object o);
}

    7、Map

public interface Map<K,V> {
    //用来存放一个键-值对Map中
    V put(K key, V value);
    //根据key(键),移除键-值对,并将值返回
    V remove(Object key);
    //将另外一个Map中的元素存入当前的Map中
    void putAll(Map<? extends K, ? extends V> m);
    //清空当前Map中的元素
    void clear();
    //根据key(键)取得对应的值
    V get(Object key);
    //判断Map中是否存在某键(key)
    boolean containsKey(Object key);
    //判断Map中是否存在某值(value)
    boolean containsValue(Object value);
    //返回所有的键(key),并使用Set容器存放
    Set<K> keySet();
    //返回所有的值(Value),并使用Collection存放
    Collection<V> values();
    //返回一个实现Map.Entry接口的元素 Set
    Set<Map.Entry<K, V>> entrySet();
}

        (1)、HashMap:无序的,非线程安全,基于哈希表的Map接口的实现,存储键值对,key是唯一的,允许使用null值和null键。用作key的对象必须要重写hashCode()和equals()方法。

        (2)、LinkedHashMap:有序的且默认为插入顺序,非线程安全,继承hashmap,初始化容量为16,容量因子为0.75。使用双向链表来维护键值对的顺序,迭代顺序与插入顺序一致,LinkedHashMap需要维护元素的插入顺序所以性能略低于HashMap,但是查询时有很好的性能,因为是基于链表实现。

        (3)、TreeMap:非线程安全,基于红黑树对key进行排序,排序方式有自定义排序和自然排序。key以TreeSet的形式存储。

        (4)、Hashtable(t小写):线程安全,初始化容量是11,容量因子是0.75,基于哈希表的Map接口的同步实现,不允许存入null键null值。synchronized是针对整张Hash表的,即每次锁住整张表让线程独占。

        (5)、时间复杂度

            HashMap、LikedHashMap增删改查时间复杂度(不考虑冲突情况):O(1)

            TreeMap增删改查时间复杂度(不考虑冲突情况):O(logN)

        (6)、HashMap、LinkedList、LinkedHashMap的Node节点

            ①、HashMap的Node节点

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    //单向链表的下一个节点
    Node<K,V> next;
    //初始化构造方法
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    ......
}

            ②、LinkedList的Node节点

private static class Node<E> {
    //list中存储的元素
    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;
    }
}

            ③、LinkedHashMap的Node节点

static class Entry<K,V> extends HashMap.Node<K,V> {
    //定义双向链表前后节点
    Entry<K,V> before, after;
    //初始化构造方法调用父类HashMap的构造方法
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值