Collection

1.概述

Java集合可分为Collection和Map两种体系

  • Collection接口:单列数据,定义存取一组对象的方法集合
    • List:元素有序、可重复的集合
    • Set:元素无序、不可重复的集合
  • Map接口:双列数据,保存具有映射关系"key-value对"的集合

2.Collection

        Collection coll = new ArrayList();
        coll.add(123);  // 自动装箱
        coll.add("AA");
        coll.add("BB");
        coll.add(new Date());
        // size获取元素个数
        System.out.println(coll.size());
        Collection coll1 = new ArrayList();
        coll.add(456);
        coll.add("CC");
        // addAll将coll1集合中的元素添加到当前的集合中,原集合会清空
        coll.addAll(coll1);
        System.out.println(coll.size());
        System.out.println(coll);
        // isEmpty判断当前集合是否为空
        System.out.println(coll1.isEmpty());
        // clear清空当前集合
        coll1.clear();
        System.out.println(coll1);

2.1.常用方法

向Collection中添加数据时,建议重写添加项的equals方法
(1)添加:

add(Object obj)
addAll(Collection coll)

(2)获取有效元素个数:

int size()

(3)清空集合:

void clear()

(4)是否是空集合:

boolean isEmpty()

(5)是否包含某个元素

boolean cotains(Object obj)  // 通过元素的equals方法来判断是否是同一个对象
boolean containsAll(Collection c)  // 也是调用元素的equals方法来比较,用两个集合的元素比较

(6)删除

boolean remove(Object obj) // 通过元素的equals方法判断是否是要删除的哪个元素,只会删除找到的第一个元素
boolean removeAll(Collection coll) // 取当前集合的差集

(7)取两个集合的交集

boolean retainAll(Collection c)  // 将交集得到结果存在于当前集合中,不影响c

(8)集合是否相等

boolean equals(Object obj)  // 如果是ArrayList,还需要考虑顺序

(9)转成对象数组

Object[] toArray()

调用Arrays.asList可以将数组转换为集合
注:

        // 如果是基本类型的数组,则整个数组会被识别为一个元素
        List list = Arrays.asList(new int[]{1, 2});
        System.out.println(list.size());    // 1

(10)获取集合对象的hash值

hashCode()

(11)遍历

iterator()  // 返回迭代器对象,用于集合遍历
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add("Tom");
        coll.add(false);
        coll.add(new Person(20, "Jerry"));
        Iterator it = coll.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

(12)forEach遍历
coll.forEach(System.out::println);
Collection接口继承java.lang.Iterator接口,此接口有一个iterator()方法,name所有实现Collection接口的集合类都有一个iterator()方法,用以返回一个实现Iterator接口的对象
Iterator仅用于遍历集合,Iterator本身并不提供承装对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的集合
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
iterator也有remove方法,可用于删除集合中数据

        while (it.hasNext()) {
            Object next = it.next();
            if ("Tom".equals(next)) {
                it.remove();
            }
        }

补充:

        // 使用增强for时修改元素并不会对当前元素产生影响,因为增强for是将每次取出的值赋给下面的obj,改变obj的值并不会对集合(或数组)产生影响
        for (Object obj : coll) {
            obj=1;
            System.out.println(obj);
        }

3.List

List集合中元素有序、且可重复,集合中每个元素都有其对应的顺序索引
List容器中的元素都对应一个整数型的序号记载,可以根据序号存取容器中的元素
JDK API中List接口的实现类常用的有:ArrayList、LinkedList、Vector

3.1.ArrayList、LinkedList、Vector三者异同

同:均实现List接口,存储数据的特点相同
不同:
ArrayList作为List接口的主要实现类(1.2版本实现),线程不安全,效率高,底层使用Object[]存储
LinkedList底层使用双向链表存储,对于频繁插入和删除操作时使用此类效率高
Vector作为List接口的古老实现类(1.0版本出现),线程安全,效率低,底层使用Object[]数组

3.2.ArrayList的源码分析

(1)jdk7情况下
ArrayList list=new ArrayList(); // 默认长度为10
默认情况下,扩容为原容量的1.5倍,同时需要将原有数组中数据复制到新数组中
结论:建议开发中使用带参构造器:ArrayList(int initialCapacity)
(2)jdk8情况下
ArrayList list=new ArrayList(); // 底层Object[] elementData初始化为{},并没有创建长度
第一次调用add时,底层才创建长度为10的数组,并将数据添加到第一个位置上
后续的添加与扩容操作与JDK7相同
(3)小结
JDK7中ArrayList创建类似于单例模式的饿汉式,JDK8创建类似于单例模式的懒汉式,延迟数组的创建,节省内存

3.3.LinkedList的源码分析

LinkedList list=new LinkedList(); // 内部声明Node类型的first和last属性,默认值为null
list.add(123) // 将123封装到Node中,创建Node对象
其中,Node定义为:

    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;
        }
    }

3.4.Vector的源码分析

JDK7与JDK8中通过构造器创建对象时,底层默认创建长度为10的数组,在扩容方面,默认扩容为原来的数组2倍的长度

3.5.List接口方法

List除从Collection集合继承的方法外,其添加一些根据索引来操作集合元素的方法
void add(int index,Object ele) 在index位置插入ele元素
boolean addAll(int index,Collection eles) 从index位置开始将eles中的所有元素添加进来
Object get(int index) 获取指定index位置的元素
int indexOf(Object obj) 返回obj在集合中首次出现的位置,如果不存在则返回-1
int lastIndexOf(Object obj) 返回obj在当前集合中末次出现的位置
Object remove(int index) 移除指定index位置的元素,并返回此元素
Object set(int index,Object ele) 设置指定index位置的元素为ele
List subList(int fromIndex,int toIndex) 返回从fromIndex到toIndex位置左闭右开的子集合,不影响集合自身

总结:常用方法
增:add(Object obj)
删:remove(Object obj) / remove(int index)
改:set(int index,Object obj)
查:get(int index)
插:add(int index,Object obj)
长度:size()
遍历:增强for循环 / iterator迭代器方式 / 普通循环

4.Set接口

  • Set接口用于存储无序的、不可重复的数据
    • HashSet作为Set接口的主要实现类,线程不安全,可以存储null值
      • LinkedHashSet作为HashSet的子类,在遍历时可以按照添加的顺序进行遍历
    • TreeSet:底层使用红黑树,放入其中的数据需要是同一个类的对象,可以按照添加对象的指定属性进行排序
      Set接口中没有额外定义新的方法,用的只是Collection中的方法
      无序性:不等于随机性,存储的数组在底层数组中并非按照数组索引的顺序添加,根据hash值确定存储的位置
      不可重复性:添加的数据不可重复,保证添加的元素按照equals判断时,不能返回true,即相同元素只能添加一个,也需要重写hashCode方法才能保证相同

4.1.添加元素的过程:以HashSet为例

向HashSet中添加元素a,首先调用元素a所在类的hashCode方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即索引位置),判断数组此位置上是否已经有元素
如果此位置上没有其他元素,则元素a添加成功
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值,如果hash不相同,则元素a添加成功,如果hash值相同,进而需要调用元素a所在类的equals方法,equals方法返回true,则元素a添加失败,equals返回false,则元素a添加成功
对于添加成功的情况而言:元素a与已经存在索引位置上的数据以链表的方式存储
JDK7:元素a放到数组中,指向原来的元素
JDK8:原来的元素在数组中,指向元素a
总结:“七上八下”

HashSet底层:数组加链表
要求:

  • 向Set中添加的数据,其类一定要重写hashCode()和equals()
  • 重写的hashCode()和equals()方法,尽可能保证一致性(相等的对象必须具有相同的散列吗)
  • 用作eqauls()方法比较的Field,都应该用来计算hashCode值
    HashSet底层使用HashMap:

4.2.LinkedHashSet

LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护两个引用,用于记录此数据前一个与后一个数据
优点:对于比较频繁的遍历操作,使用LinkedHashSet要高于HashSet

4.3.TreeSet

向TreeSet中添加的数据,要求是相同类的对象
两种排序方式:自然排序(Comparable)、定制排序(Comparator)
自然排序中,TreeSet根据Comparable接口的compareTo方法比较是否相同,而不是根据equals判断是否相同

        // 其中User实现Comparable接口
        TreeSet<User> treeSet = new TreeSet<>();
        treeSet.add(new User("Tom", 12));
        treeSet.add(new User("Jerry", 32));
        treeSet.add(new User("Jim", 2));
        treeSet.add(new User("Mike", 65));
        treeSet.add(new User("Jack", 33));

定制排序中,在TreeSet构造器中传入Comparator实现类参数,此时TreeSet会根据Comparator中的compare方法进行比较

        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User && o2 instanceof User) {
                    User user1 = (User) o1;
                    User user2 = (User) o2;
                    return user1.getName().compareTo(user2.getName());
                }
                throw new RuntimeException("输入的数据类型不匹配");
            }
        };
        TreeSet<User> treeSet = new TreeSet<>(com);

5.Map接口


HashMap作为主要实现类,效率高,线程不安全,可以存储null的key和value
LinkedHashMap在HashMap的基础上添加双向引用,保证在遍历map元素时,可以按照添加的顺序实现遍历,原因:在原有的HashMap底层结构基础上,添加一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap
TreeMap可以按照添加的键值对进行排序,实现排序遍历(按照key进行自然排序或者定制排序),底层使用红黑树
Properties常用于处理配置文件,key和value都是String类型

HashMap在JDK1.7及之前,使用数组+链表的形式,在JDK1.8中使用数组+链表+红黑树的格式

Hashtable作为古老实现类,效率低,线程不安全,不能存储null的key和value

6.Collections工具类

reverse(List) 反转list中的元素顺序
shuffle(List) 对list集合元素进行随机排列

posted @ 2021-08-08 23:18  kanaliya  阅读(448)  评论(0)    收藏  举报