Java集合(六)

1 .概述

        Java的集合类是一个特别有用的工具类,用于存储数量不等的多个对象,并可以实现常用的数据结构,如栈、队列。Java集合大致分为:Set、List和Map三种体系。Set:无序、不可重复。List:有序、可以重复。Map:具有映射关系的集合。JDK1.5之后又增加了Queue体系集合,代表队列集合实现。
        集合类注意负责保存、盛装其他数据,因此集合类也称容器类。所有集合位于java.util包下。
        Java的集合类主要由两个接口派生而出。Collection和Map是Java集合的根接口。两个接口又保安韩了一些子接口或实现类。

在这里插入图片描述

在这里插入图片描述


2.Collection和Iterator接口

        Collection接口是List、Set和Queue接口的父接口,该接口里面定义的方法既可以操作Set集合,也可以操作List和Queue集合。

Collection接口定义操作如下:
  • boolean add(Object o);:添加一个元素,添加成功返回true。
  • boolean addAll(Collection c);:把集合c里所有元素添加到指定集合里,如果集合对象被添加操作改不了返回true。
  • void clear();:清除集合里的所有元素,将集合长度变为0.
  • boolean contains(Object o);:返回集合是否包含指定元素。
  • boolean containsAll(Collection c);:返回集合里是否包含集合c里所有元素。
  • boolean isEmpty();:返回集合是否为空,当长度为0时返回true;否则false。
  • Iterator iterator();:返回一个Iterator对象,用于遍历集合里的元素。
  • boolean remove(Object o);:删除集合中指定元素o,当集合中包含了一个或者多个元素o时,这些元素将会被删除,该方法返回true。
  • boolean removeAll(Collection c);:删除指定集合里的所有元素,如果删除一个或一个以上的元素,返回true。
  • boolean retainAll(Collection c);:从集合中删除集合c里不包含的元素,如果该操作改变了调用该方法的集合,返回true。
  • int size();:返回集合里的元素个数。
  • Object[] toArray();:该方法把集合转换成一个数组,所有集合元素都变成对于的数组元素。
使用Iterator接口遍历集合元素
使用Iterator遍历集合主要用到下面三种方法:
  • boolean hasNect();:如果被迭代得集合元素还没有被遍历,返回true。
  • Object next();:返回集合里下一个元素。
  • void remove();:删除集合里上一次next方法返回的元素。
Collection books=new HashSet();
books.add("格林童话");
books.add("安徒生童话");
Iterator it=books.iterator();
while(it.hasNext){
	String book=(String)it.next();
	System.out.println(book);
}

        Iterator必须依附于Collection对象,Iterator提供了2个方法来迭代访问Collection集合里的元素,并可以通过remove方法删除集合中上次next方法返回的集合元素。

        使用Iterator迭代块内,也就是Iterator迭代Collection集合过程中修改Collection集合,会反射依次。Iterator迭代器采用的是快速失败机制(fail-fast),一旦在迭代过程中,检测到该集合已经被修改(通常程序中其他线程被修改),程序立即引发ConcurrentModificationException异常。 .

使用foreach循环变量集合元素
        JDK1.5之后提供了foreach循环来迭代访问集合元素更加便捷。
Collection books=new HashSet();
books.add("格林童话");
books.add("安徒生童话");
for(Object obj: books){
	String book=(String)obj;
		System.out.println(book);
}

        从语法上看,使用foreach循环来迭代访问Collection集合元素更简洁,这也是JDK1.5之后的foreach集合循环带来的优势。与Iterator迭代访问集合想类似的是,foreach循环的迭代变量也不是集合元素本身,系统只是依次把集合元素的值赋给迭代变量,因此,在foreach循环中修改迭代变量的值也没有任何意义。
        同样地,在foreach循环访问集合元素时,该集合也不能被改变,否则会引发ConcurrentModificationException异常。

1.1 Set接口

        Set集合类似于一个罐子,把对象添加到Set集合是,Set集合无法记住添加元素的顺序,所以Set集合里面的元素不能重复。如果把两个相同的元素加入同一个set集合里,则添加操作失败,add方法返回false,且新元素不会被加入。
        Set判断两个对象相同不是使用==而是根据equals,如果两个对象用equals方法比较返回true,Set就不会接受这两个对象;反之,只要这两个对象的用equals返回的是false,Set就会接受这两个对象(甚至这两个对象是同一个对象,Set也可以把他们当做两个对象来处理)。

Set books=new HashSet();
// 添加一个字符串对象
books.add(new String("安徒生童话"));
// 再次添加一个字符串对象
// 因为两个字符串对象通过equals方法比较相等,所以添加失败,返回false。
boolean result=books.add(new String("安徒生童话"));
//输出结合,只有一个元素
System.out.println(books);

        上面这段代码只输出一个元素,显然上面两个被添加的对象使用==返回的是false,使用equals返回true,所以添加失败了。

1.1.1 HashSet类

        HashSet是Set接口的典型实现,大多数时候使用这个实现类。HashSet按Hash算法来存储集合中的元素,因此有很好的存取和查找性能。

HashSet具有以下特点:
  • 不能保证元素的排列顺序,顺序可能发生变化。
  • HashSet不是同步的,如果多个线程同时访问一个Set集合,如果多个线程同时访问一个HashSet,如果2条或2条以上线程同时修改一个HashSet集合时,必须通过代码保证其同步。
  • 集合元素值可以是null。

        HashSet判断两个元素是否相等的标准是比使用equals()hashCode()判断相等。
        当想HashSet存入一个数据时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值来决定该对象在HashSet中存储位置。如果两个元素通过equals方法比较返回true,但是他们的hashCode值不同,HashSet也会将他们存储在不同的位置,也可以成功添加。

public class HashSetTest {

    public static void main(String[] args) {
        HashSet books=new HashSet();
        //分别向books集合中添加两个A对象。两个B对象,两个C对象
        books.add(new A());
        books.add(new A());
        books.add(new B());
        books.add(new B());
        books.add(new C());
        System.out.println(books);
    }
}

class A{

    @Override
    public boolean equals(Object obj) {
        return true;
    }
}
class B{
    @Override
    public int hashCode() {
        return 1;
    }
}

class C{
    @Override
    public int hashCode() {
        return 2;
    }

    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

运行结果:
在这里插入图片描述
        如果两个对象通过hashCode()方法返回相同的值,但是通过equals()比较返回true,这将导致HashSet将会把这两个对象保存在HashSet的不同位置,从而两个对象都可以添加成功,这与Set集合的规则有点出入。如果HashSet中有两个hashCode值相同,将会导致性能下降。

hashCode方法对于HashSet的作用是什么?

        我们先要理解hash(也被翻译为哈希、散列)算法的功能:它能保证通过一个对象快速找到另一个对象。hash算法的价值在于速度,它可以保证查询得到快速执行。当需要查询集合中某个元素时,hash算法可以直接根据该元素的值得到还元素保存在何处,从而可以让程序员快速找到该元素。为了理解,我们先看数组(数组是所有能存储一组元素最快的数据结构):数组可以包含多个元素,每个元素也有索引,如果需要访问某个数组元素,只需要提供该元素的索引,该索引即指出了该元素再数组内存区里的存储位置。
        表面上看,HashSet集合里的元素都没有索引,实际上当程序向HashSet集合中添加元素时,HashSet会根据该元素的hashCode值来决定它的存储未知子——也就是说,每个元素的hashCode就是它的“索引”。
        为什么不直接使用数组,还需要使用HashSet呢?因为数组元素的索引是连续的,而且数组长度是固定的,无法自由添加数组的长度。而HashSet就不一样了,HashSet采用每个元素的hashCode作为其索引,从而可以自由增加HashSet的长度,并可以根据元素的hashCode值访问元素。因此,从头HashSet中访问元素时,HashSet先计算该元素的hashCode值(也及时调用对象的hashCode()方法的返回值),然后直接到该hashCode对应的位置去取出该元素——这就是HashSet速度很快的原因。

        HashSet中每个能存储元素的“槽位(slot)”通常称为“桶(bucket)”,如果有多个元素的hashCode相同,但他们通过equals方法比较返回false,就需要在一个“桶”里放多个元素,从而导致性能下降。

重写hashCode()方法的基本原则:
  • 当两个对象通过equals方法比较返回去true时,这两个对象的hashCode应该相等。
  • 对象中用作equals比较标准的属性,都应该用来计算hashCode值。
不同类型属性取得hashCode值的方式
属性类型计算方式
booleanhashCode=(f?0:1);
整数类型(byte、short、char、int)hashCode=(int)f;
longhashCode=(int)(f^(f>>>32));
floathashCode=Float.floatToIntBits(f);
doublelong l=Double.doubleToLongBits(f);
hashCode=(int)(l^(l>>>32));
普通引用类型hashCode=f.hashCode();

        如果像一个HashSet里面添加两个相同类的对象时,添加的时候这两个类的属性值不一样,添加完之后,修改属性值,使得这两个对象的属性值完全相同(即两个对象通过equals方法比较返回true,两个对象的hashCode值也相等),这就有可能导致HashSet中包含两个相同的对象。

 public static void main(String[] args) {
      HashSet hs=new HashSet();
      hs.add(new D(6));
      hs.add(new D(9));
      hs.add(new D(7));
      hs.add(new D(-2));
      //打印HashSet集合,集合元素没有重复
      System.out.println(hs);
      //取出第一个元素
        Iterator it=hs.iterator();
        D first= (D) it.next();
         //修改第一个元素的属性值,修改为与地三个元素值相同,因为我们重写了hashCode方法所以也就是说它们的hashCode也会相等
        first.count=7;//(1)
        //再次输出HashSet集合,有重复元素
        System.out.println(hs);
        //删除count为7的D对象
        hs.remove(new D(7));
        //输出删除后的元素,
        System.out.println(hs);
    }
}

class D{
    public int count;

    public D(int count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return "D{" +
                "count=" + count +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof D){
            D d=(D)o;
            if (this.count==d.count){
                return true;
            }
        }
        return false;

    }

    @Override
    public int hashCode() {
        return this.count;
    }
}

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

         当向HashSet中添加可变对象时,必须十分小心。如果修改HashSet集合中的对象,有可能导致该对象与集合中其他对象相等,从而导致HashSet无法准确访问集合里的元素

LinkedHashSet

        LinkedHashSet是HashSet的子类,LinkedHashSet也是根据元素hashCode值来决定元素存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是一插入顺序保存的。也就是说,当遍历LinkedHashSet集合里元素时,HashSet将会按元素的添加顺序来访问集合里的元素。



1.1.2 TreeSet

        TreeSet是SortedSet接口的唯一实现,正如SortedSet名字暗示一样,TreeSet可以确保元素处于排序状态。

TreeSet几个方法:
  • Comparator comparator():返回当前Set使用的Comparator,或者返回null,表示以自然方式排序。
  • Object first():返回集合中第一个元素。
  • Object last():返回集合中最后一个元素。
  • Object lower(Object e):返回集合中位于指定元素(即小于指定元素的最小元素)。
  • Object higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素)。

        TreeSet不是根据元素的的插入顺序排序的,而是根据元素的世界值排序。
        HashSet集合采用hash算法来决定元素的存储位置不同。TreeSet采用红黑树的数据结构队元素进行排序。

        TreeSet支持两种排序方法,自然排序定制排序:

自然排序

        自然排序:TreeSet会调研集合元素的compareTo(Object obj)来比较元素之间的大小,然后将集合按升序排序。
        Comparable接口定义了一个int compareTo(Object obj)方法,实现该接口的类必须实现该方法。

obj1.compateTo(obj2);

        返回0:表示obj1与obj2相等。
        返回负整数:表明obj1<obj2
        返回正整数:表明obj1>obj2

Comparable接口常用类
BigDecimal、BigInteger、所有数值型对于包装类:按它们对应数值的大小进行比较。
Character:按字符的UNICODE值比较。
String:按字符串中的字符的UNICODE值比较。
Boolean:true对应的包装类实例大于false对应的包装类实例。
Date、Time:后面的时间、日期>前面的时间、日期
public class TestTreeSet {

    public static void main(String[] args) {
        TreeSet ts1=new TreeSet();
        ts1.add("hello");
        ts1.add(" world");
        ts1.add("love");
        System.out.println(ts1);

        TreeSet ts2=new TreeSet();
        ts2.add(new Err());//(1)
    }
}
class Err{

}

运行结果:

在这里插入图片描述
其中第19行如下:
在这里插入图片描述
        在上面代码中,向一个TreeSet集合添加一个字符串对象,操作正常。但是当添加一个我们自定义的引用对象时,却失败了。这是因为TreeSet是一个有序集合,添加时需要通过Comparable的compareTo(Object obj)方法比较大小。

        大部分类实现compareTo(Object obj)方法,都需要将被比较对象强制转为相同类型,因为只有相同类的两个实例才会比较大小。因此,TreeSet只允许添加同一个类的实例,也就是说TreeSet中添加1的应该是同一个类的对象,否则也会引发ClassCastException异常。
        当一个对象被加入TreeSet集合中时,TreeSet调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑算法决定它的存储位置。如果两个对象通过compareTo(Object obj)比较相等,TreeSet认为他们应该存储在同一个位置。

        如果向TreeSet中添加一个可变对象之后,并且后面程序修改了该对象的属性,是得它与其他对象大小顺序发生了改变,但TreeSet不会再调整它们的顺序,甚至可能导致TreeSet中保存这两个对象。当试图删除对象是,TreeSet也会删除失败。TreeSet可以删除没有被修改属性,且不与其他属性的对象重复的对象;但是不能修改意见修改过的重复的属性的对象。

定制排序

        TreeSet的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要实现定制排序,例如降序排序,则可以使用Comparator接口。该接口里有一个int compare(T o1,T o2)方法,用于比较o1和o2大小。

  • 返回0,相等
  • 返回负整数,o1<o2
  • 返回正整数,o1>o2

         使用Comparator对象来实现TreeSet的定制排序时,异常不可以向TreeSet中添加类型不同的对象,否则也会引发ClassCastException异常。使用定制排序时,TreeSet对集合元素排序时,不过集合本身的大小,而是Comparator对象负责集合元素的排序规则。

HashSet和TreeSet的选择:
        HashSet和TreeSet是Set的两个典型实现,如何选择HashSet和TreeSet?HashSet的性能总是比TreeSet好(特别是常用的添加、查询元素等操作),因为TreeSet需要额外的红黑数算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
        HashSet还有一个子类:LinkedHashSet,对于普通插入、删除操作,LinkedHashSet比HashSet要略微慢一点;这是由于维护链表所带来的额外开销所造成的,不过,因为有了链表,变量LinkedHashSet会更快。

1.1.3 EnumSet类

        EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有值必须都是指定枚举类型的枚举值,该枚举类型在创建EnumSet时,显式地或隐式地指定顺序。EnumSet集合元素也是有序的,EnuSet一枚举类在该Enum类的定义顺序来决定集合元素的顺序。
        EnumSet不予许加入null元素,如果传入null元素将会抛出NullPointerException异常,TreeSet也不能加入null元素,但是Set和HashSet可以加入null元素。
        EnumSet类没有保留任何构造器来创建该类的实例。

常用的static方法创建EnumSet对象:
  • static EnumSet allOf(Class elementType);创建一个保护指定枚举类的所有枚举值的EnumSet集合。
  • static EnumSet complementOf(EnumSet s);:创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet。
  • static EnumSet copyOf(Collection c):使用一个普通集合来创建EnumSet集合。
  • static EnumSet copyOf(EnumSet s):创建一个与指定EnumSet具有相同元素类型,相同集合元素的EnumSet。
  • static EnumSet noneOf(Class elementType):创建一个元素类型为指定枚举类型的空EnumSet。
  • static EnumSet of(E first,E...rest):创建一个保护一个或者多个枚举值的EnumSet,传入的枚举值必须属于同一个枚举类。
  • static EnumSet range(E from,E to):创建一个包含从from枚举值,到to枚举值范围内所有枚举值的EnumSet集合。

        EnumSet是所有Set实现类中性能最好的,但是它只能保存同一个枚举类的枚举值作为集合元素。
        Set的三个实现类HashSet、TreeSet、EnumSet都是线程不安全的。如果有多条线程同时访问一个Set集合,并且超过一条线程修改该Set集合,则必须动手保证该Set集合的同步性。通常可以通过Collection工具类的synchronizedSortedSet方法来“包装”该Set集合。此操作最好在创建时进行,防止对Set集合的意外非同步访问。



1.2List

        List集合像一个数组,它可以记住每次添加元素的顺序,只是List集合的长度可变。List集合允许使用重复元素,List集合默认是按元素的添加顺序设置元素的索引。
在这里插入图片描述

1.2.1 List接口和ListIterator接口

        List集合作为Collection接口的子接口,可以使用Collection里的所有方法。List是有序集合,增加的一些方法:

  • void add(int index,Object element):将元素element插入在List集合的index处。
  • boolean addAll(int index,Collection c):将集合c里的所有元素插入在List集合的index处。
  • Object get(int index):根据索引取元素值。
  • int indexOf(Object o):返回对象o在List集合中的位置索引。
  • int lastIndexOf(Object o):返回对象o在List集合中最后一次出现的位置索引。
  • Object remove(int index):删除并返沪index索引所出的元素。
  • Object set (int index,Object element):将index索引处的元素替换成element对象返回新元素。
  • List subList(int fromIndex, int toIndex):返回索引fromIndex(包含)到索引toIndex(不包含)所处集合元素组成的子集合。范围:[fromIndex,toIndex)。

        List额外提供了一个ListIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口基础上增加了:

  • boolean hasPrevious():返回迭代器关联的集合时候还有上一个元素。
  • Object previous():返回迭代的上一个元素。
  • void add():在指定位置插入一个元素。
public class TestListIterator {
    public static void main(String[] args) {
        String[] books={"格林童话","安徒生童话"};
        List bookList=new ArrayList();
        for (int i=0;i<books.length;i++){
            bookList.add(books[i]);
        }
        //使用ListIterator,迭代输出
        ListIterator listIterator=bookList.listIterator();

        System.out.println("==============正向迭代");
        while (listIterator.hasNext()){
            System.out.println(listIterator.next());
        }

        System.out.println("============反向迭代============");
        while (listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }

    }
}

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

1.2.2 ArrayList和Vector实现类

        ArrayList和Vector是List的两个典型实现类。它们都是基于数组实现的List类,它们的对象都有用一个capacity属性,这个属性表示他们所封装的Object[]数组长度。当想ArrayList和Vector添加剂元素时,他们的capacity都会自动增加。当需要这两个集合中添加大量元素时,可以使用ensureCapacity方法一次性增加capacity,这样可以减少分配空间的次数,提高性能。
        如果知道ArrayList或者Vector集合需要保存多个个元素,可以在创建它们时就指定它们的capacity大小。如果创建空的ArrayList或者Vector集合不指定capacity属性,capacity属性默认值是10。

  • void ensureCapacity(int minCapacity):将集合的capacity增加minCapacity。
  • void trimToSize():调整集合的capacity当前大小。可以调用该方法减少没有使用的存储空间。

        Vector里有些重复的方法,这些方法中方法名更短的属于后面新增的方法,方法名长的属于原来的方法。Java改写原来的Vector方法,将其方法名缩短是为了简化编程。而ArrayList开始就作为List的主要实现类,因为没有那些方法名很长的方法。实际上,Vector具有很多缺点,尽量少用Vector实现类。

ArrayList和Vector的区别

? ArrayList是线程不安全的,而Vector是线性安全的。当多条线程访问同一个ArrayList集合时,超过一条线程修改了ArrayList集合,就必须手动保存集合的同步性。因为Vector是线程安全的,无需程序保证集合的同步性,所有Vector的性能要比ArrayList性能低。

Vector的子类Stack

        还提供了一个Stack类,模拟数据结构中的“栈”,“栈”是一种“先进后出(后进先出)”(LIFO,Last In First Out)的一种容器。最后“push”进栈的元素,最先被“pop”出栈。

Stack类的几个方法
  • Object peek():返回”栈“的第一个元素,但是并不让该元素”pop“出栈。
  • Object push():返回栈顶的第一个元素,并且将该元素”pop“出栈。
  • void push(Object item):将一个元素"push"进栈,最后一个进”栈“的元素总是为与栈顶。
固定长度的List

        操作数组的一个工具类:Arrays,该工具提供了asList(Object… a)方法,该方法可以将一个数组或一个指定个数的对象转换成一个List集合,这个List集合既不是ArrayList的实现类的对象,也不是Vector实现类的对象,而是Arrays的内部类ArrayList的对象。
        Arrays.ArrayList是个固定的长度的List集合,只能遍历,不能删除,添加。若修改这个集合,会引发UnsupportedOperationException异常。



1.3 Queue接口

        模拟数据结构的队列,“先进先出”(First In First Out)。

常用方法:
  • void add(Object e):将制定元素加入此队列的尾部。
  • Object element():获取队列头部元素,但不删除该元素。
  • boolean offer(Object e):将指定元素加入此队列的尾部。当使用有限容量限制的队列时,此方法通常比add(Object e)方法更好。
  • Object peek():获取队列头部元素,但不是删除该元素。如果队列为空,返回null。
  • Object poll():获取队列头部的元素,并删除该元素。如果队列为空,返回null。
  • Object remove():获取队列头部的元素,并删除该元素。

1.3.1 LinkedList实现类

        LinkedList即是List接口的实现类,也是Deque接口的实现类,而Deque接口又是Queue的子接口。这意味着LinkedList既是一个链表,又是一个双向队列。

常用方法
  • void addFirst(Object e):将指定元素插入该双向队列的开头。
  • void addLast(Object e):将指定元素插入到双向队列的末尾。
  • Iterator descendingIterator():返回该双向队列对于的迭代器,该迭代器以逆向顺序迭代队列元素。
  • Object getFirst():获取,但不删除队列中第一个元素。
  • Object getLast():获取,但不删除双向队列的最后一个元素。
  • boolean offerFirst():将指定元素插入该队列开头。
  • boolean offerLast():将指定元素插入该队列的末尾
  • Object peekFirst():获取,但不删除该队列开头的元素,如果队列为空,返回null。
  • Object peekLast():获取,但不删除该队列末尾的元素,如果队列为空,返回null。
  • Object pollFirst:获取并删除该队列开头的元素,如果队列为空,返回null。
  • Object pollLast:获取并删除该队列末尾的元素,如果队列为空,返回null。
  • Object pop():pop出该双向队列所表示栈中第一个元素。
  • void push(Object e):将一个元素push进该双向队列所表示的栈中(即该双向队列的头部)。
  • Object removeFirst():获取并删除该双向队列的第一个元素。
  • Object removeFirstOccurrence(Object o):删除该双向对队列的第一次出现的元素o。
  • Object removeLast():获取并删除该双向队列的最后一个元素。
  • Object removeLastOccurrence(Object o):删除该双向对队列的最后一次出现的元素o。

        从上面可以看出LinkedList不仅可以当作双向队列使用,也可以当成“栈”使用,因为还包含pop和push方法。除此之外还可以当成List集合使用,因为实现了List接口。

LinkedList:链表形式保存集合中元素,插入、删除占优势,随机访问集合性能较差。
ArrayList、Vector:以数组形式保存集合中元素,随机访问集合元素有较好性能,插入性能较差。Vector实现线程同步,所以各方面性能都有所下降。

各容器性能比较
&实现机制随机访问排名迭代操作排名插入操作排名删除操作排名
数组连续内存区保存元素1不支持不支持不支持
ArrayList内部以数组保存2222
Vector内部以数组保存3333
LinkedList内部以链表保存4111

1.3.2 PriorityQueue实现类

        PriorityQueue是一个比较标准的队列实现类,之所以说它是比较标准的队列实现,而不是绝对标准的队列实现是因为:PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排列。因此当调用peek方法或者poll方法来取队列中的元素时,并不是取出最先进入队列的元素,而是取出最小的元素。
        PriorityQueue不允许插入null元素,它还需要对队列元素进行排序。

两种排序方式:
  • 自然排序:采用自然排序必须实现Comparable接口,且一个集合中应该是同一个类的对实例,否则会产生ClassCastException异常。
  • 定制排序:创建PriorityQueue队列时,传入一个Comparator对象,该对象赋值对队列中所有元素排序。采用定制排序不需要实现Comparable接口。



1.4 Map

        Map集合也像一个罐子,只是它里面的每项数据都由两个值组成(Key-value)。
在这里插入图片描述
官方文档
在这里插入图片描述6

常用方法:
  • 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 put(Object key,Object value):添加一个key-value对,如果当前Map中已有一个与该Key相等的Key-value对,则新的key-value对会覆盖原来的key-value对。
  • void putAll(Map m):指定Map中的key-value对复制到本Map中。
  • Object remove(Object key):删除指定key的key-value对,并返回被删除key所关联的value,如果该key不存在,返回null。
  • int size():返回Map中的key-value对数。
  • Collection values():返回Map所有的value组成的Collection。
Map接口的一个内部接口Entry,三个方法:
  • K getKey:返回Entry包含的key值。
    在这里插入图片描述
  • V getValue():返回Entry包含的value值。

在这里插入图片描述

  • V setValue(V value):设置该Entry里面包含的value值,并返回新设置的value值。

在这里插入图片描述

1.4.1 HashMap和Hashtable实现类

在这里插入图片描述
在这里插入图片描述
        HashMap和Hashtable都是Map接口的典型实现类,他们完全类似于ArrayList和Vector的关系:Hashtable(Vector)是个古老的类,Hashtable从JDK1.0就出现了,我们从这个类的命名就可以看出很不规范,但是后面因为太多地方用到Hashtable了,改动会影响很大,因此并没有使用驼峰命名。

Hashtable和HashMap存在两点典型区别:
  • Hashtable是一个线程安全的Map实现的,但HashMap是线程不安全的,因此HashMap性能会比Hashtable更好些。
  • Hashtable不允许使用null作为key和value,如果使用null放入Hashtable中,会引发NullPointerException异常;但HashMap语序存在一个为null的Key,多个为null的value。
判断相等
  • HashMap和Hashable判断key相等的标准是通过equals比较返回true,并且hashCode值也相等。
  • HashMap和Hashable判断value相等的标准是通过equals比较返回true即可。

尽量不要使用可变对象作为HashMap、Hashtable的key,如果一定要这么做,就不会修改这个key的可变对象。

        HashMap有一个子类LinkedHashMap,这个类使用双向链表维护key-value对的次序,迭代顺序与key-value对插入的顺序一致,也因为这个它比HashMap性能低。
        Properties类是Hashtable类的子类,该类在处理文件属性(Windows操作平台上的ini文件就是一直属性文件),也可以吧Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入文件属性,也可以吧属性文件中的属性名=属性值加载到Map对象中由于属性文件的属性名、属性值只能是字符串类型,所以Properties里的key和value都是字符串类型。

在这里插入图片描述

Properties三个方法:
修改

  • String getProperty(String key)
    Searches for the property with the specified key in this property list.
  • String getProperty(String key, String defaultValue)
    Searches for the property with the specified key in this property list.
  • Object setProperty(String key, String value)
    Calls the Hashtable method put.
    读写
    在这里插入图片描述
    在这里插入图片描述

1.4.2 SortedMap接口和TreeMap实现类

        SortedMap接口是Map接口的一个子接口,TreeMap是SortedMap的一个的实现类。
在这里插入图片描述
在这里插入图片描述

TreeMap的两种排序方式
  • 自然排序:TreeMap的Key必须实现Comparable接口,且所有Key为1同类型,否则出现ClassCastException。
  • 定制排序:创建TreeMap时,传入一个Comparator对象,赋值对TreeMap中所有key进行排序。定制排序不要求实现Comparable接口。
源码构造器:
在这里插入图片描述
在这里插入图片描述
构造器官方文档
在这里插入图片描述
详细方法及其他信息查看 TreeMap官方文档

1.4.3 WeakHashMap实现类

        WeekHashMap是Map的一个实现类,同时继承了AbstractMap类。HashMap的key保留是对实际对象的强引用,也就是说这个对象不会被销毁,也不会自动删除。而WeakHashMap是一种弱引用,这意味着如果该HashMap对象所有Key所引用的对象没有被其他类强引用时,很有可能会被垃圾回收机制回收,也可能会自动删除这些key对应的key-value对象。
在这里插入图片描述

        如果使用WeakHashMap的key保留对象的弱引用,就不要让该对象有其他的强引用了,不然也就失去了WeakHashMap的意义了。

1.4.4IdentityHashMap实现类

        Identity类时Map接口的一个实现类,这个类与HashMap相似。不同点在于HashMao批判两个对象相等是通过equals判断,而IdentityHashMap则是通过==判断两个对象相等。

在这里插入图片描述

1.4.5 EnumMap实现类

        EnumMap是AbstractMap这个抽象类的子类。这个类的所有key都是同个枚举类中的枚举值。创建EnumMap时,必须显式或者隐式指定它对应的枚举值。
在这里插入图片描述

        EnumMap内部以数组的形式保存,所以这种实现形式十分紧凑、高效。
        根据key的自然排序(枚举值在枚举类中定义的顺序)来维护key-value的顺序。可以通过keySet()entrySet()values()方法遍历。
        EnumMap不允许使用null作为key值,但是可以运行使用null作为value值。

构造方法官方文档
在这里插入图片描述
从文档可以看出,在创建EnumMap对象时,就必须显式或者隐式指定枚举值关联对象。

EnumMap官方文档



1.5 HashSet和HashMap的性能选择

        hash表里面可以存储元素的位置称为“桶(bucket)”,一般情况下,一个桶是存储一个元素的。当然在发生hash冲突(存在相同的hash值)时,一个桶就会存储多个元素。实际上,hash算法根据hashCode值直接找到桶的存储位置,继而才桶中取出该元素,因此,一个桶中只存取一个元素性能是最好的。

        HashSet、HashMap、Hashtable都是使用hash算法决定其元素的存储的(对于HashMap来说是key的存储)。

hash表包含的属性:
  • 容量(capacity):创建hash表时,的数量。
  • 初始化容器(initial capacity):创建hash表时,桶的数量。HashMap和HashSet都运行在构造器中指定初始化量。
  • ** 尺寸(size)**:当前hash表中记录的数量。
  • 负载因子(load factor)负载因子=size/capacity。负载因子为0,表示该hash表为空。轻负载的hash表具有冲突少、适宜插入与查询的特点。



1.6 操作集合工具类Collections

        Collections是一个操作Set、List、Map等集合的工具类。有排序,查询,修改等操作。这些操作方法都是静态的。
在这里插入图片描述
在这里插入图片描述

1.6.1排序操作

  • reverse(List<?> list)
    Reverses the order of the elements in the specified list.
  • shuffle(List<?> list)
    Randomly permutes the specified list using a default source of randomness.
  • sort(List list)
    Sorts the specified list into ascending order, according to the natural ordering of its elements.
  • static void sort(List list, Comparator<? super T> c)
    Sorts the specified list according to the order induced by the specified comparator.
  • static void swap(List<?> list, int i, int j)
    Swaps the elements at the specified positions in the specified list.

其他操作参考官方文档

1.6.2 同步控制

        Collections类提供了多个synchronizedXxx方法,返回指定集合对象对应的同步对象,从而可以解决多线程并发访问集合时的线程安全问题
        HashSet、ArrayList、HashMap线程不安全的。如果超过一条线程试图访问就会出错。

//示例创建同步集合对象
List list=Collections.synchronizedCollection(new ArrayList());

1.6.4 设置不可变集合

        

  • emptyXxx():返回一个空的不可变的集合对象。
  • singletonXxx():返回一个包含指定对象(只有一个或一项元素)的、不可变的集合对象,可以是List、Set、Map。
  • unmodifyableXxx():返回指定集合对象的不可变视图,可以是List、Set、Map。

1.7 繁琐的接口Enumeration

        Enumeration即可是Iterator迭代器的古老版本,JDK1.0就开始存在,(Iterator是JDK1.2出现的)。
在这里插入图片描述

只有两个方法:
在这里插入图片描述
hasMoreElements:如果此迭代器还有剩下的元素,返回true。
nextElement返回迭代器的下一个元素。如果还有的话,没有抛出异常。
  • Enumeration保留是因为照顾以前那些古老的程序。
  • ArrayList、HashMap等不支持Enumeration迭代器操作,Enumeration迭代器只适合变量Vector、Hashtable这样古老的集合。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值