集合框架 | 概念与 API

本贴最后更新于 1517 天前,其中的信息可能已经渤澥桑田

集合(存储的是对象)

集合主要有 Collection 接口和 Map 接口派生

  • Collection

1582765537330.png

其中粗线全出的 Set 和 List 接口是 Collection 接口派生的两个子接口,他们分别代表了无序集合和有序集合。Queue 是 Java 提供的队列实现。

  • Map

1582765838852.png

Set 集合类似一个罐子,当把一个元素添加到 Set 中,Set 集合无法记住这个元素的顺序,所以 Set 里的元素不能重复。List 集合非常像一个数组,它可以记住每次添加元素的顺序,且 List 的长度可变。Map 集合也像一个罐子,知识它里面的每项数据都由两个值组成。

常用的集合

HashSet,TreeSet,ArrayList,ArrayDeque,LinkedList,HashMap,TreeMap 等。

Collection 和 Iterator

Collection

Collection 是 List,Set,Queue 的父接口,该接口的方法适用于这几个

  • boolean add (Object o):该方法用于向集合里面添加一个元素。如果集合对象被添加操作改变了返回 True(所以返回类型位 boolean 而不是 void,为的是提供一个检查 Set 元素是否添加成功的方式)
  • boolean addAll (Collection c):该方法把集合 c 的所有元素添加到指定集合。如果集合对象被添加操作改变了返回 True
  • void clear():清楚集合里的所有元素,将长度变为 0
  • boolean contains(Object 0):返回集合里是否包含指定元素
  • boolean contains(Collection c):返回集合里是否包含集合 c 里面的所有元素
  • boolean isEmpty():返回集合是否为空。当长度为 0 时返回 true,否则返回 false。
  • Iterator iterator():返回一个 Iterator 对象,用于遍历集合里面的元素。
  • boolean remove(Object o):删除集合中指定元素 o,当集合中包含了一个或多个 o 时,方法只删除第一个符合条件的元素,然后返回 true。
  • boolean removeAll(Collection c):从集合中删除集合 c 里不包含的元素(相当于把该集合变为和集合 c 的交集),如果该操作改变了调用该方法的集合,则返回 true
  • int size():该方法返回集合里的元素的个数。
  • Object[] to Array():该方法把集合转换成一个数组,所有集合元素变成对应的数组元素。

Iterator

用于遍历集合元素

  • boolean hasNext():如果被迭代的元素还没有被遍历完,则返回 true
  • Object next():返回集合里面的下一个元素
  • void remove():删除集合里上一次 next 方法返回的元素
  • void forEachRemaining(Consumer action):java8 新增的默认方法,该方法可以用 lambda 表达式来遍历集合元素

Set

HashSet

  • 元素不能重复
  • 按 Hash 算法来存储集合中的元素
  • 具有良好的存取和查找性能
  • 不能保证元素的排列顺序,顺序可能与添加顺序不同
  • 不是同步的,如果有两个或以上的线程同时修改 HashSet,则必须通过代码来保持同步
  • 集合元素可以为 null

存入元素时,HashSet 调用该对象的的 HashCode 值,然后根据他们的值决定他们 HashSet 中的存储位置。如果两个元素通过 equals()方法比较返回 true,但他们的 hashCode 方法的返回值不相等,HashSet 将会把他存储在不同的位置,依然可以添加成功。如果 hashCode 相同,而 equals 不同,也会认为是两个对象(通过链式结构来保存多个对象)

HashSet 中每个能存储元素的"槽位(slot)"通常被称为"桶"(bucket)

如果有多个元素的 hashcode 值相同,但他们通过 equals 方法比较返回 false,就需要在一个“桶”

里面放多个元素,这样会导致性能下降。

重写 hashcode 的步骤
  • 把对象内每个有意义的实例变量(即每个参与 equals()方法比较标准的实例变量)计算出一个 int 类型的 hashCode 值。

1582771637566.png

  • 用第一步算出来的多个 hashcode 值组合计算出一个 hashcode 的值

  • 为了避免每个 hashcode 相加导致的相等,可以位每个 hashcode 值乘以一个质树后再相加。

注意

当程序把可变对象添加到 HashCode 中后,精良不要去修改集合元素参与计算 hashcode 和 equals 的实例遍历。

LinkedHashSet

  • 是 HashSet 的子类
  • 也是根据 hashcode 来决定元素的存储位置,但他同时使用链表来维持元素的次序。
  • 性能略低于 HashSet
  • 虽然使用链表记录了集合元素的添加顺序,淡 LinkedHashSet 依然是 HashSet,因此他依然不允许元素重复。

TreeSet

  • 是 SortedSet 的实现类,TreeSet 可以确保集合元素处于排序状态。
  • 与,HashSet 相比。TreeSet 提供了几个额外的方法
    • Comparator Comparator():如果 TreeSet 采用了定制排序,则该方法返回定制排序所使用的 Comparator;如果 TreeSet 采用了自然排序,则返回 null;
    • Object first():返回集合中的第一个元素
    • Object last():返回集合中的最后一个元素
    • Object lower(Object e):返回集合中位于指定元素之前的元素(就是返回小于指定元素的最大值),参考元素不需要是 TreeSet 中的元素
    • Object higher(Object e):返回集合中位于指定元素之后的元素(就是返回大于指定元素的最小值),参考元素不需要是 TreeSet 中的元素
    • SortedSet subSet(Object fromElement , Object toElement):返回此 Set 的子集,范围从 fromElement (包含)到 toElement(不包含)
    • SortedSet headSet(Object toElement):返回此 Set 的子集,由小于 toElement 的元素组成
    • SortedSet tailSet(Object toElement):返回此 Set 的子集,由大于 toElement 的元素组成
自然排序
  • TreeSet 放入一个对象时会调用集合元素的 compareTo(Object obj)方法来比较集合元素的大小关系,然后按升序排序,这种方式就是自然排序。
  • java 提供了一个 Compareable 接口,该接口定义了一个 compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法。实现了该方法的对象与另一个对象进行比较的时候,比如 obj1.compareTo(obj2),如果该方法返回 0,说明 obj1 与 obj2 相等,如果返回一个正整数表明 obj1 大于 obj2,如果返回一个负数,表示 obj1 小于 obj2。

1582774653739.png

package TreeSetTest;

import java.util.TreeSet;


class Student implements Comparable {
    /**
     * 学生的姓名
     */
    private String name;
    /**
     * 学生的年龄
     */
    private Integer age;
    
	/*··························
	* 省略的getter和setter
	*/··························
 
    @Override
    public int compareTo(Object o) {
       Student student = (Student)o;
       return this.getAge() > student.getAge() ? 1 :
               this.getAge() < student.getAge() ? -1 : 0  ;
    }
}


/**
 * @author yaoqiuhong
 * @create 2020-02-27 11:39
 * @description
 */
public class TreeSetDemo {
    public static void main(String[] args) {
        // 创建一个学生对象1
        Student student1 = new Student();
        student1.setName("yqh");
        student1.setAge(21);
        // 创建一个学生对象2
        Student student2 = new Student();
        student2.setName("yyw");
        student2.setAge(20);
        // 创建一个TreeSet来存储学生对象
        TreeSet treeSet = new TreeSet();
        /*
         * 如果Student没有实现Comparable中的compareTo方法,那么下面将Student加入
         * treeSet中会出现java.lang.ClassCastException
         */
        treeSet.add(student1);
        treeSet.add(student2);
    }
}
  • 大部分对象在实现 compareTo(Object obj)的时候,需要将 Object 对象强转成本类对象经行某个属性的比较。而又由于在向 TreeSet 中添加元素的时候会通过 compareTo(Object obj)方法来比较,所以在向其中添加不同类的对象的时候,也会引发 java.lang.ClassCastException.所以需要注意的是:向 TreeSet 中添加的应该是同一个类的对象
  • 当把一个对象加入 TreeSet 集合中,TreeSet 调用该对象的 compareTo(Object obj)方法跟其他对象比较大小,然后根据红黑树结构找到他的存储位置。如果两个对象根据 compareTo(Object obj)方法返回的值为 0,新对象将无法添加到 TreeSet 中。
  • 对于 TreeSet 而言,他判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj)比较返回的值是否为 0,如果为 0,就认为相等,如果不为 0,就认为不相等。
  • 当需要把一个类的对象放到 TreeSet 中时,应该保证该类中的 compareTo(Object obj)返回 0 时 ,他的 equals()方法返回 true
  • 当改变了 TreeSet 集合里添加的元素的属性后,这将导致它与其他对象的大小顺序发生了变化,淡 TreeSet 不会再次调整他们的顺序,甚至导致 TreeSet 中保存的这两个对象通过 compareTo(Object obj)方法比较返回 0。(正常情况下,为 0 时,后一个不会被插入)
  • 当 TreeSet 中的元素的属性(跟 compareTo 方法中的比较有关的字段)有被修改过后,那么被修改的属性就不能对修改过的元素进行 remove 操作。只能对没有改变的元素进行 remove 操作。值得注意的是:对没有被修改的的元素进行 remove 操作后,TreeSet 会对集合中的元素从新索引(不是从新排序),接下来就可以删除 TresSet 中的所有元素了。
定制排序
  • TreeSet 的自然排序是根据集合元素的大小,TreeSet 将他们以升序排序,如果要实现定制排序,例如通过降序排序(当然自然排序通过反写 compareTo 方法的比较逻辑也可以实现),则可以通过 Comparator 接口的帮助。该接口里面包含一个 int commpare(T o1, T o2 )方法,该方法用于比较 o1 和 o2 的大小。如果该方法返回 0,说明 o1 与 o2 相等,如果返回一个正整数表明 o1 大于 o2,如果返回一个负数,表示 o1 小于 o2。
  • 如果需要实现定制排序,则需要在创建 TreeSet 集合对象的时候,提供一个 Comparator 对象与 TreeSet 集合关联,由该 Comparator 对象负责集合元素的排序逻辑。由于 Comparator 时一个函数式接口,因此可使用 lambda 表达式来替代 Commparator
  • 同样不可以插入相同的元素,不然会由 ClassCastException 异常
package TreeSetTest.TreeSetTest;

import java.util.TreeSet;

class Persion {
    /**
     * 人的姓名
     */
    private String   name;

    /**
     * 人的年龄
     */
    private Integer  age;

	/*··························
	* 省略的getter和setter
	*/··························

    @Override
    public String toString() {
        return "Persion{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


/**
 * @author yaoqiuhong
 * @create 2020-02-27 14:15
 * @description
 */
public class TresSetTest {
    public static void main(String[] args) {
        // 创建对象人1
        Persion persion1 = new Persion();
        persion1.setName("YQH");
        persion1.setAge(21);
        // 创建对象人2
        Persion persion2 = new Persion();
        persion2.setName("YYW");
        persion2.setAge(20);

        //创建TreeSet对象
        TreeSet treeSet = new TreeSet((o1,o2) ->{
            Persion p1 = (Persion)o1;
            Persion p2 = (Persion)o2;
            return p1.getAge() < p2.getAge() ? 1 :
                    p1.getAge() > p2.getAge() ? -1 : 0 ;
        });

        treeSet.add(persion1);
        treeSet.add(persion2);

        System.out.println("21"+((Persion)treeSet.first()).getAge());

    }

}

EnumSet

  • 是一个专门为枚举设计的集合类,EnumSet 中的所有元素都必须时指定枚举类型的指定值。该枚举类型是在创建 EnumSet 时显示或隐式地指定。Enum 的集合元素也是有序的,EnumSet 以枚举值在 Enum 类内定义顺序来决定集合元素的顺序
  • 以位向量的形式存储,这种存储形式非常紧凑,高效,英雌 EnumSet 都西昂占用内存很小,而且运行效率很好。尤其是进行批量操作(如调用 containsAll()和 retainAll 方法)时,如果参数也是 EnumSet 集合,则该批量操作的执行速度也非常快。
  • 不允许加入 null 元素,如果试图插入 null 元素,EnumSet 将抛出 NullPointerException 异常。如果是想判断 EnumSet 是否包含 null 元素或试图删除 null 元素都不会抛出异常,只是删除操作将返回 false。
  • 没有提供构造器来创建该类的实例,程序应该通过他提供的类方法来创建 EnumSet。Enum 提供了如下常用方法来创建 EnumSet 对象。
    • EnumSet allOf(Class elementType):创建一个包含指定枚举类型所有枚举值的 EnumSet 集合。
    • EnumSet complementOf(EnumSet s):创建一个其元素类型与指定 EnumSet 里元素类型相同的 EnsumSet 集合,新 EnsumSet 集合包含原 EnumSet 集合所不包含的,此枚举剩下的枚举值(即新的 EnumSet 集合和原集合的集合元素加起来就是该枚举类的所有枚举值)
    • EnumSet copyOf(Collection c):使用一个普通集合来创建 EnumSet 集合
    • EnumSet copyOf(EnumSet s):创建一个与指定 EnumSet 具有相同元素类型,相同集合元素的 EnumSet 集合
    • EnumSet noneOf(Class elementType):创建一个元素类型为指定枚举类型的空 EnumSet。
    • EnumSet of(E first, E... rest):创建一个包含一个或多个枚举值的 EnumSet 集合,传入的多个枚举值必须属于同一个枚举类
    • EnumSet range(E from, E to):创建一个包含从 from 枚举值到 to 枚举值范围内所有枚举值 EnumSet 集合。
package enumSetTest;

import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;


/**
 * 季节枚举类
 * */
enum Season{

    SPRING(1),SUMMER(2),FALL(3),WINTER(4) ;

    /**
     * 季节的代码
     * */
    private int code;

    Season(int code){
        this.code = code ;
    }

    public int getCode(){
        return code;
    }
}


/**
 * @author yaoqiuhong
 * @create 2020-02-27 20:26
 * @description
 */
public class EnumSetTest {
    public static void main(String[] args) {

        // 通过allOf(Class elementType)创建一个Season类型全部枚举值的EnumSet
        EnumSet enumSet1 = EnumSet.allOf(Season.class);
        System.out.println("enumSet1:" + enumSet1);

        // 通过noneOf(Class elementType)创建个Season类型的空EnumSet
        EnumSet enumSet2 = EnumSet.noneOf(Season.class);
        System.out.println("enumSet2:" + enumSet2);

        // 添加Season的元素到EnumSet中
        enumSet2.add(Season.WINTER);
        enumSet2.add(Season.SUMMER);
        System.out.println("enumSet2:" + enumSet2);

        // 通过of(E first, E... rest)指定枚举值创建Season类型的EnumSet
        EnumSet enumSet3 = EnumSet.of(Season.SUMMER , Season.WINTER);
        System.out.println("enumSet3:" + enumSet3);

        // 通过range(E from, E to)创建一个从SPRING到WINTER的EnumSet
        EnumSet enumSet4 = EnumSet.range(Season.SUMMER , Season.WINTER);
        System.out.println("enumSet4:" + enumSet4);

        // 通过complementOf(EnumSet  s)创建一个enumSet4的补集的EnumSet
        EnumSet enumSet5 = EnumSet.complementOf(enumSet4);
        System.out.println("enumSet5:" + enumSet5);

        // 创建一个元素为Season值得HashSet
        Collection c = new HashSet();
        c.add(Season.SUMMER);
        c.add(Season.WINTER);


         // 通过copyOf(Collection c)来复制c中的所有元素来创建一个EnumSet(Collection对象c中的所有元素都必须是同一个枚举类的枚举值)
        EnumSet enumSet6 = EnumSet.copyOf(c);
        System.out.println("c:"+c+" enumSet6:"+enumSet6);

        // 通过copyOf(EnumSet  s)复制一个EnumSet
        EnumSet enumSet7 = EnumSet.copyOf(enumSet6);
        System.out.println("enumSet6:"+enumSet6+" enumSet7:"+enumSet7);

    }

}

各个 Set 得得性能比较

  • HashSet 得性能总比 TreeSet 好(特别是最仓用类得添加/查询元素等操作)因为 TreeSet 需要额外得红黑树算法来维护集合得次序。只有当需要一个保持排序得 Set 时,才应该使用 TreeSet。
  • LinkedHashSet 对于普通的插入,删除操作,LinkedHashSet 比 HashSet 要略慢一点,这是由于维护链表所带来的额外开销造成的,但由于有了链表,遍历 LinkedHashSet 更快。
  • EnumSet 是所有 Set 实现类中性能最好的。但他只能保持同一个枚举类的枚举值作为集合元素
  • Set 的三个实现类,HashSet,TreeSet,enumSet 都是线程不安全的。

List

  • List 也是 Collection 的子接口,可以使用所有 Collection 中的方法。而且由于 List 是有序集合,英尺 List 集合增加了一些感觉索引来操作集合的方法。
    • void add(int index , Object element):将元素 element 插入到 index 处
    • boolean addAll(int index , Collection c):将集合 c 所包含的所有元素插入到 List 集合的 index 处
    • Object get(int index):返回集合 index 索引处的元素
    • int indexOf(Object o):返回对象 o 在 List 集合中第一次出现的位置
    • int lastIndex(Object o):返回对象 o 在 List 集合中最后一次出现的位置
    • Object remove(int index):删除并返回 Index 处的元素
    • Object set(int index , Object element):将 index 索引处的元素替换成 element 对象,返回被替换的旧元素。(只能改变,不能向后面添加)
    • List subList(int formIndex , int toIndex):返回从索引 formIndex(包含) 到索引 toIndex(不包含)的所有元素组成的子集合
    • void replaceAll(UnaryOperator operator):根据 operator 指定的计算规则重新设置 List 集合的所有元素
    • void sort(Comparator c):根据 Comparator 参数对 List 集合的元素排序。

通过 equals()方法来判断两个元素是否相等

replaceAll 和 sort 的演示

    package List;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author yaoqiuhong
     * @create 2020-02-27 23:19
     * @description
     */
    public class ListTest {
        public static void main(String[] args) {
            // 创建List对象
            List list = new ArrayList();
            /*
            * 添加元素
            * */
            list.add("如果那两个字");
            list.add("没有颤抖");
            list.add("我不会发现");
            list.add("我难受");
            System.out.println("list中元素插入顺序:"+list);
            // 使用Comparator的lambda表达式对List集合排序
            list.sort(((o1, o2) -> {
                String s1 = (String)o1;
                String s2 = (String)o2;
                return s1.length() - s2.length();
            }));
            System.out.println("list中元素通过sort排序后顺序:"+list);
    
            // 使用Lambda表达式控制使用每个字符串的长度作为新的元素集合
            list.replaceAll(ele -> ((String)ele).length());
            System.out.println("list中元素通过replaceAll替换后元素为:"+list);
        }
    }

​ ListIterator

与 Set 只提供一个 Iterator 不同,List 还额外提供一个 ListIterator 方法,该方法返回一个 ListIterator 对象,ListIterator 接口继承了 Iterator 接口,提供了专门操作 List 的方法。ListIterator 接口在 Iterator 接口上增加了一下方法。

  • boolean haspervious():返回该迭代器关联的集合是否还有上一个元素
  • Object previous():返回该迭代器的上一个元素
  • void add(Object o):在指定位置插入一个元素

ListIterator 相比于 Iterator 增加了向前迭代的功能(Iterator 只能向后迭代)。而且 ListIterator 还能通过 add()方法向 List 中添加元素(Iterator 只能删除元素)

ArrayList and Vector

  • 都是基于数组实现的 List 类,所以 ArrayList 和 Vector 类都封装了一个动态的,允许再分配的 Object[]数组
  • 使用 initialCapacity 参数来设计该数组的长度,当向 ArrayList or Vector 添加的元素超过了数组的长度的时候,ArrayList 或 Vector 的 initialCapacity 会自动增加。
  • 对于普通场景,我们无需关心 initialCapacity 的大小。但如果向 ArrayList or Vector 添加大量元素的时候,可以使用 ensureCapacity( int minCapacity)方法一次性地增加 initialCapacity。这就可以减少分配的权重,从而提高性能!
  • 如果创建空的 ArrayList or Vector 时没有指定 initialCapacity 参数,则 Object[]数组的长度默认为 10
  • void ensureCapacity( int minCapacity):将 ArrayList or Vector 集合时的 Object[]数组长度增加大于或等于 minCapacity 值。
  • void trimToSize():调整 ArrayList or Vector 集合的 Object[]数组的个数为当前元素的个数。该方法可以减少集合占用的存储空间。
  • ArrayList or Vector 的显著区别就是:ArrayList 时线程不安全的,Vector 则是线程安全的。这里需要注意即使要保证 ArrayList 时线程安全的,也不推荐使用 Vector。这时候可以使用 Collections 工具类,它可以将一个 ArrayList 变成线程安全的。
Stack
  • Vector 还提供一个 Stack 子类,它用于模拟"栈"这种数据结构。与其他集合一样,进栈出栈的元素都是 Object,因此从栈中取出元素后必须进行类型转换,除非你只是使用 Object 具有的操作。提供的方法
    • Object peek():返回"栈"的第一个元素,但并不将该元素"pop"出栈
    • Object pop():返回"栈"的第一个元素,但并将该元素"pop"出栈
    • void push(Object item):将一个元素"push"进栈,最后一个进"栈"的元素总是位于"栈"顶
    • 跟 Vectors 是一个较老的类,它同样是线程安全的,性能较差的。因此应该少用 Stack,如果需要用"栈"这种数据结构,则可以考虑使用 ArrayDeque

Arrays.ArrayList

  • 有一个操作数组的工具类:Arrays,该工具类里提供了 asList(Object... a)方法,该方法可以把一个数组或指定个数的对象转换成一个 List 集合,这个 List 集合既不是 ArrayList 实现类也不是 Vector 的实现类,而是 Arrays 的内部类 ArrayList 的实例。

  • Arrays.ArrayList 是一个固定长度的 List 集合,程序只能遍历访问该集合的元素,不可台南佳,删除该集合里的元素。

    package arrays;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * @author yaoqiuhong
     * @create 2020-02-28 00:36
     * @description
     */
    public class ArraysDemo {
        public static void main(String[] args) {
            // 利用Arrays.asList获取List
            List list = Arrays.asList("如果那两个字","没有颤抖","我不会发现我难受");
            System.out.println(list);
            // 遍历元素
            list.forEach(System.out::println);
            // 试图增加,删除元素都会引发UnsupportedOperationException异常
            list.add("增加元素会抛出UnsupportedOperationException异常");
            list.remove("移除元素会抛出UnsupportedOperationException异常");
        }
    }
    

Queue

  • 用于模拟队列这种数据结构(元素先进先出)
  • 新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素。筒仓,队列不允许所及访问队列中的元素
  • 定义了如下方法:
    • void add(Object e):将指定的元素加入此队列的尾部
    • Objec element():获取队列头部的元素,但是不删除该元素
    • boolean offer(Objec e):将指定元素加入此队列的尾部。当使用有容量限制的队列时,此方法通常比 add(Object e)方法更好。
    • Objec peek():获取队列头部的元素,但是不删除该元素。如果队列为空,返回 null
    • Objec poll():获取队列头部的元素,并删除该元素,如果此队列为空,则返回 null
    • Objec remove():获取队列头部的元素,并删除该元素
    • Queue 有一个 PriorityQueue 实现类。除此之外,Queue 还有一个 Deque 接口,Deque 代表一个“双端队列”,双端队列可以同时从两端来添加,删除元素,因此 Deque 的实现类即可以当成队列使用,也可当成栈使用。java 为 Deque 提供了 ArrayDeque 和 LinkedList 两个实现类

PriorityQueue

  • 不按元素的插入顺序保存元素,二十按元素的大小从新排序,因此通过 peek()方法或者 poll 方法取出队列中的元素时,并不是去除最先进入的元素而是取出最小的元素。
  • 不允许插入 null 元素
  • 两种排序
    • 自然排序:元素实现 Comparable 接口
    • 定制排序:创建 PriorityQueue 队列时,闯入一个 Comparaor 对象。
    • PriorityQueue 对元素的要求与 TreeSet 对元素的要求基本一致。

Deque 接口与 ArrayDeque 实现类

  • Deque 接口是 Queue 接口的字接口,Deque 代表一个“双端队列”,双端队列可以同时从两端来添加/删除元素。提供如下方法允许从两端操作队列:
    1582823575256.png

  • 使用栈这种数据结构的时候推荐使 ArrayDeque

LinkedList

LinkedList 是 List 接口的实现类,可以根据索引来随机访问集合中的元素。与此同时,LinkedList 海慧寺心安了 Deque 接口,可以被当成双端队列来使用。

1582823931076.png

各种线性表的性能分析

1582824030113.png

Map

  • Map 用于保存具有映射关系的数据,Map 里面保存着两组值,以组值用于保存 Map 里的 Key,另外一组值用于保存 Map 里的 Value,key 和 value 都可以是任何类型的数据。Map 的 Key 不允许重复,即同一个 Map 的两个 key 通过 equals 方法总是返回 false。
  • key 和 value 是一对一的关系,即通过指定的 key,总能找到唯一的,确定的 value。如果把 Map 的两组值拆开来看:
    • Map 里面的所有 Key 放在一起来看,他们就组成了一个 Set 集合(所有 Key 没有顺序,key 与 key 之间不能重复)实际上 map 里面确实包含了一个 KeySet 方法,用于返回 Map 里面所有 Key 组成的 Set 集合
    • Set 与 Map 的关系十分密切,虽然 Map 中放的元素是 Key-value 对,Set 集合中存放的元素是单个对象,但如果把 Key-value 对中的 value 当成 key 的附庸:key 在哪里,value 就在哪里。这样就可以对待 Set 一样来对待 Map 了。事实上 Map 提供了一个 Entry 内部类来封装 Key-Value 对,而计算 Entry 存储时只考虑 Entry 封装的 Key。从 Java 的源码来看,java 先是实现了 Map,然后通过包装一个所有 Value 都为 null 的 Map 就实现了 Set 集合。
    • Map 里面的所有 Value 放在一起来看,他们又非常类似一个 List:元素与元素之间可以重复,每个元素可以根据索引来查找,知识 Map 中的索引不再使用整数值,而是使用另一个对象作为索引。如果要从 List 集合去除元素,则需要提供该元素的数字索引;如果需要重 Map 里面取出元素,则需要提供该 Value 的 Key 索引。因此,Map 有时候也被称为字典,或者关联数组。
  • Map 提供了如下接口
    • void clear():删除该 Map 对象中的所有 Key-value 对
    • boolean containsKey(Object key):查询 Map 中是否包含指定的 Key,如果包含返回 True
    • boolean containsValue(Object value):查询 Map 中是否包含一个或者多个的 value,如果包含返回 True
    • Set entrySet():返回 Map 中包含的 Key-value 对所组成的 Set 集合,每个集合元素都是 Map.Entry(Entry 是 Map 的内部类)对象
    • Object get(Object key):返回指定的 key 所对应的 value,如果此 Map 中不包含该 Key,则返回 null
    • boolean isEmpty():查询该 Map 是否为空(即不包含任何的 Key-value 对)如果为空则返回 true
    • Set ketSet():返回该 Map 中所有 key 组成的 Set 集合
    • Object put(Object key , Object value):添加一个 Key-value 对,如果 dangqianMap 中已有一个与该 Mao 相等的 Key-value 对,则新的 Key-value 对会覆盖原来的 Key-value 对。
    • void putAll(Map m):将指定的 Map 中的 Key-value 对复制到本 Map 中
    • Object remove(Object key):删除指定的 key-value 对,返回被删除 key 所关联的 value,如果该 key 不存在,则返回 null
    • boolean remove(Object key , Object value):java8 新增的方法,删除指定 key,value 所对应的 key-value 对。如果删除成功,返回会 true,否者返回 false
    • int size():返回该 Map 中的 key-value 的个数
    • Collection values():返回该 Map 里所有 value 组成的 Collection

Entry

  • 是 Map 集合的一个内部类,该类封装了一个 key-value 对。Entry 包含如下三个方法
    • Object getKey():返回该 Entry 中包含的 key 值
    • Object getValue():返回该 Entry 里包含的 Value 值
    • Object setValue(V value):设置该 Entry 里包含的 Value 值,并返回新设置的 value 值。

HashMap and Hashtable

  • Hashtable 线程安全,HashMap 线程不安全
  • Hashtable 不允许使用 null 作为 key 和 value。Hashtable 允许使用 null 作为 key 和 value(由于 Map 的 key 不能重复,所以 HashMap 里最多只有一个 key-value 对的 Key 为 null。)
  • 为什么是 Hashtable 而不是 HashTable。Hashtable 是一个很老的类,它的命名甚至没有遵守 Java 的命名规范:每个单词的首字母都应该大写。(后来大量的程序中使用了 Hashtable,所以这个类名就没修改为 HashTable)
  • 为了成功的在 HashMap,Hashtable 中存储,获取对象,用作 Key 的对象必须实现 HashCode 方法和 equals 方法。判断两个 Key 是否相等的标准 是:通过 equals()方法比较返回 true,两个 key 的 hashCode 值也相等。判断两个 value 是否相等的标准通过 equals 方法比较返回 true
  • 与 HashSet 一样,如果使用可变对象作为 HashMap 的 key,并且修改了作为 Key 的可变对象,那么就会出现与 HashSet 一样的情形。

LinkedHashMap

  • 与 HashSet 有一个 LinkedHashSet 子类类似,HashMap 也有一个 LinkedHashMap 子类:LinkedHashMap 也是使用双向链表来维护 key-value 对的顺序(只需要考虑 key 的次序).
  • 使用 LinkedHashMap 可以避免对 HahMap,Hashtable 里的 key-value 排序(只需要插入的时候保持顺序即可),同时又可避免使用 TreeMap 所增加的成本。
  • 性能略低于 HashMap

Properties

  • Properties 是 Hashtable 的子类,该对象在处理属性文件时特别方便。可以把 Map 对象和属性文件关联起来。从而把 Map 对象中的 key-value 对写入属性中,也可以把属性文件中的“属性名 = 属性值”加载到 Map 对象中。由于属性文件中的属性名和属性值都是字符串。所以 Properties 的 key-value 都是字符串类型。改类提供了如下方法:

    • String getProperty(String key):获取 Property 中指定属性名对应的属性值,类似与 Map 的 get(Object key)方法
    • String getProperty(String key , String defaultvalue):与上一个方法类似。并且如果不存在指定的 key 时,则方法指定默认值
    • Object setProperty(String key , String value):设置属性值,类似与 Hashtable 的 put()方法。
    • void load(InputStream instream):从属性文件中加载 key-value 对,把加载到的 key-value 对追加到 Properties 里(Properties 是 Hashtable 的子类,不保证 key-value 之间的次序)
    • void store(OutputStream out ,String comments):将 Properties 中的 key-value 对输出到指定的属性文件中。

TreeMap

  • 是 SortedMap 接口的实现类。TreeMap 就是一个红黑树结构,每个 key-value 对即作为红黑树的一个节点。TreeMap 存储 key-value 对,树妖对 key 节点进行排序。排序方法有两种。自然排序和定制排序(与 TreeSet 相似)

1582938415654.png

WeakHashMap

与 HashMap 类似,与 HashMap 的区别在于 HashMap 的 key 保留了实际对象的强引用,这意味着,只要 HashMap 对象不被销毁,该 HashMap 的所有 key 所引用的对象就不会被垃圾回收,HashMap 也不会自动删除这些 key 对应的 key-value 对:但 WeakHashMap 的 key 只保留了实际对象的弱引用,这意味着 WeakHashMap 对象的 key 所引用的对象没有被其他强引用变量所引用,则这些 key 所引用的对象可能被垃圾回收,WeakHashMap 也可能自动删除这些 key 对应的 key-value 对

IdentityHashMap

与 HashMap 类似,与 HashMap 的区别在于,它在处理两个 key 相等时比较特殊,在 IdentityHashMap 中,当且仅当两个 key 严格相等(key1 == key2)时,IdentityHashMap 才会认为两个 key 相等。

EnumMap

  • EnumMap 内部以数组形式保存
  • EnumMap 根据 key 的自然顺序(即枚举值中的定义顺序)来维护 key-value 对的顺序。
  • 不允许使用 null 作为 key,但允许 null 作为 value。如果试图使用 null 插入 key,那么就会抛出 NPE。如果只是查询是否包含 null 或者只是删除值为 null 的 key,那么都不会抛出 NPE
  • 创建一个 EnumMap 必须指定一个枚举类。EnumMap enumMap = new EnumMap(EnumClassName.class)

各 Map 的性能分析

  • HashMap 通常比 Hashtable 要快(Hashtable 是一个古老并且线程安全的集合)
  • TreeMap 通常比 HashMap,Hashtable 要慢(尤其在插入,删除时更慢),应为 TreeMap 使用红黑树管理 key-value 对(红黑树的每个节点都是一个 key-value 对)
  • 使用 TreeMap 的好处是可以让 Key-value 对总是处于有序状态,无需专门排序。当 TreeMap 被填充后,就可以调用 keySet,取得由 key 组成的 Set,然后使用 toArray()方法生成 Key 的数组。接下来使用 Arrays 的 binarySearch()方法在已排序的数组中快速的查询对象
  • LinkedHashMap 比 HashMap 要慢一点,因为他需要维护链表来保持 Map 中 Key-value 时的添加顺序
  • 一般建议使用 HashMap,因为 HashMap 就是为快速查询设计的(HashMap 地城其实也是采用数组来存储 key-value 对)

HashMap 与 HashSet 性能比较

1582940932896.png
1582940957135.png

Collections

排序

  • void reverse(List list):反转指定 List 集合中元素的顺序
  • void shuffle(List list):对 List 集合进行随机排序
  • void sort(List list):根据元素的自然顺序对 List 集合的元素按升序排序
  • void sort(List list , Comparator c):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
  • void swap(List list, int i, int j):将指定 List 集合中的 i 处元素和 j 处元素进行交换
  • void rotate(List list, int distance):当 distance 为正数时,将 list 集合的后 distance 各元素“整体”移到前面。当 distance 为负数时,将 list 集合的前 distance 各元素整体移到后面

查找替换

  • int binarySerach(List list , Object key):使用二分法搜索指定的 List 集合,以获得指定对象在 List 集合中的索引。如果要使该方法可以正常工作,则必须保证 List 中的元素已经处于有序状态。
  • Object max(Collection coll):根据元素的自然顺序,返回给定集合中的最大元素
  • Object max(Collection coll , Comparator comp):根据 Comparator 指定顺序,返回给定集合中的最大元素
  • Object min(Collection coll):根据元素的自然顺序,返回给定集合中的最小元素
  • Object min(Collection coll , Comparator comp):根据 Comparator 指定顺序,返回给定集合中的最小元素
  • void fill(List list , Object obj):使用指定元素 obj 替换指定 List 集合中的所有元素
  • int frequency(Collection c , Object o):返回指定集合中指定元素出现次数
  • int indexOfSublist(List source , List target):返回子 List 对象在父 List 对象中第一次出现的位置索引:如果父 List 中没有出现这样的子 List,则返回-1
  • int lastIndexOfSublist(List source , List target)返回子 List 对象在父 List 对象中最后一次出现的位置索引:如果父 List 中没有出现这样的子 List,则返回-1
  • boolean replaceAll(List list ,Object oldVal ,Object newVal):使用一个新值 newVal 替换 LIst 对象的所有旧值 oldVal

同步控制

  • Collection 类中提供了多个 synchronizedXxx() 方法,该方法可以将只当集合包装成线程同步的集合,从而解决并发访问集合时的线程安全问题
  • java 中常用的集合的实现类 HashSet,TreeSet,ArrayList,ArrayDeque,LInkedList,HashMap 和 TreeMap 都是线程不安全的。
  • Collection 可以通过 Collection c = Collections.synchronizedCollection(new Collection实现类) 创建对应的线程安全版本
  • List 可以通过 List list = Collections.synchronizedList(new List实现类) 创建对应的线程安全版本
  • Map 可以通过 Map map = Collections.synchronizedMap(new Map实现类) 创建对应的线程安全版本
  • Set 可以通过 Set set = Collections.synchronizedSet(new Set实现类) 创建对应的线程安全版本

设置不可变集合

  • emptyXXX():返回一个空的,不可变的集合对象,此处的集合既可以是 List,也可以是 SortedSet。Set,还可以时 Map,SortedMap

  • singletonXXX():返回一个只包含指定对象(只有一个或一项元素)的,不可变的集合对象,此处的集合即可以时 List,也还可以是 Map

  • unmodifiableXxx():返回指定集合对象的不可变试图,此处的集合计可以是 List,也可以是 Set。SortedSet,还可以是 Map。SortedMap 等。

  • 上面三个方法的参数都是原有的集合类型,返回值是该集合的“只读”版本。如果对其修改则会出现 UnsupportedOperationException 异常。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3168 引用 • 8207 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...