处以Map结尾的类之外,其它都实现了Collection接口,而以Map结尾的类实现了Map接口。
Java中的具体集合
集合类型 | 描 述 |
---|---|
ArrayList | 一种可以动态增长和缩减的索引序列 |
LinkedList | 一种可以在任何位置进行高效地插人和删除操作的有序序列 |
ArrayDeque | 一种用循环数组实现的双端队列 |
HashSet | 一种没有重复元素的无序集合 |
TreeSet | 一种有序集 |
EnumSet | 一种包含枚举类型值的集 |
LinkedHashSet | 一种可以记住元素插入次序的集 |
PriorityQueue | 一种允许高效刪除最小元素的集合 |
HashMap | 一种存储键/值关联的数据结构 |
TreeMap | 一种键值有序排列的映射表 |
EnumMap | 一种键值属于枚举类型的映射表 |
LinkedHashMap | 一种可以记住键/值项添加次序的映射表 |
WeakHashMap | 一种其值无用武之地后可以被垃圾回收器回收的映射表 |
IdentityHashMap | 一种用==而不是用equals比较键值的映射表 |
9.2.1链表
数组和数组列表都有一个重大缺陷:从数组中间删除一个元素要付出很大代价,其原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动。在数组的中间插入一个元素也是如此。
数组再连续的存储位置上存放对象引用,但链表却将每个对象存放在独立的结点总。每个节点还存放着序列中下一个结点的引用。
在Java程序设计语言中,所有链表实际上都是双向链接的(double linked)–即每个结点还存放着指向前驱结点的引用。
链表是一个有序集合,每个对象的位置十分重要。
LinkedList.add方法将对象添加到链表的尾部。
只有对自然有序的集合使用迭代器添加元素才有实际意义。
ListIterator接口有两个方法,可以用来反向遍历链表。
E previous(); boolean hasPrevious();
LinkedList类的listIterator方法返回一个实现了ListIterator接口的迭代对象。 ListIterator< E> listIterator();
set方法用一个新元素取代调用next或previous方法返回的上一个元素。
ListIterator< String> iter = list.listIterator(); String oldValue = iter.next();//return first element iter.set(newValue);//sets first element to newValue.
一定要注意ListIterator的forEachRemaining方法,在使用时需要明白ListIterator.next的作用,如果forEachRemaining方法之前调用了ListIterator.next会输出什么?
如果迭代器发现它的集合被另外一个迭代器修改了,或者是被该集合自身的方法修改了,就会抛出一个异常。
List< String> list = ...; ListIterator< String> iter1 = list.listIterator(); ListIterator< String> iter2 = list.listIterator(); iter1.next(); iter1.remove(); iter2.next();//throws Exception
链表不支持快速地随机访问。如果要查看链表中第n个元素,就必须从头开始,越过n-1个元素。LinkedList对象根本不做任何缓存位置信息的操作;
LinkedList.get()//效率不高;
java.util.LinkedList< E>
- void addFirst(E element)
- void addLast(E element)
将某个元素添加到列表的头部或尾部。
java.util.ListIterator< E>
- void set(E element)
用新的元素取代next或previous上次访问的元素。如果在next或previous上次调用之后列表结构被修改了,将抛出一个IllegalStateException异常。
9.2.3散列集(hash set)
散列表为每个对象计算一个整数,称为散列码(hash code)。
散列码是由对象的实例域产生的一个整数。
在Java中,散列表用链表数组实现。每个列表被称为桶(bucket)。要想查找列表中对象的位置,就首先计算它的散列码,然后与桶的总数取余,所得到的结果就是保存这个元素的桶的索引。HashSet散列集;
当桶被占满的时候,这种现象称为散列冲突(hash collision)。这时,需要用新对象与桶中的所有对象进行比较,查看这个对象是否已经存在。
Java SE8中,桶满时会从链表变为平衡二叉树。
再散列。
装填因子。
不关心集合中元素的顺序时才应该使用HashSet。
9.2.4树集
TreeSet(红黑树)类与散列集十分类似,不过它比散列集有所改进。树集是一个有序集合。每次将一个元素添加到树中时,都被放置在正确的排序位置上。因此,迭代器总是以排好序的顺序访问每个元素。
从Java SE 6起,TreeSet类实现了NavigableSet接口。这个接口增加了几个便于定位元素以及反向遍历的方法。
java.util.NavigableSet< E>
- E higer(E value)
- E lower(E value)
返回大于value的最小元素或小于value的最大元素,没有则返回null。- ceiling(E value)
- floor(E value)
返回大于等于value的最小元素或小于等于value的最大元素,没有则返回null。- E pollFirst()
- E pollLast
删除并返回集合中的最大或最小元素,集合为空时返回null。- Iterator< E> descendingIterator()
返回一个按照递减顺序遍历集中元素的迭代器。
9.2.5队列与双端队列
队列可以在头尾部插入元素,有两个端头的队列,即双端队列,不支持在队列中间添加元素。在Java SE6中引入了Deque接口,并由ArrayDeque和LinkedList类实现。
java.util.Queue< E>
- boolean add(E elment);//报错
- boolean offer(E e);//false
- E remove()//删除并返回头部元素,如果空,则报错
- E poll()//如果空,则为null;
- E element()//返回头部,如空,报错
- E peek()//返回null
java.util.Deque< E>
- void addFirst(E e)
- void addLast(E e)
- boolean offerFirst(E e)
- boolean offerLast(E e)
将给定的对象添加到双端队列的头部或尾部。如果队列满了,前两个方法将抛出一个异常,后面两个返回false。- E getFirst()
- E getLast()
相当于Queue的element()方法。
9.2.6 优先级队列
优先级队列(priority queue)中的元素可以按照任意的顺序插人,却总是按照排序的顺序进行检索。也就是说,无论何时调用remove方法,总会获得当前优先级队列中最小的元素。然而,优先级队列并没有对所有的元素进行排序。如果用迭代的方式处理这些元素,并不需要对它们进行排序。优先级队列使用了-一个优雅且高效的数据结构,称为堆(heap)。 堆是一个可以自我调整的二叉树,对树执行添加(add)和删除( remore2) 操作,可以让最小的元素移动到根,而不必花费时间对元素进行排序。
与est样,一个优先级队列既可以保存实现了Copamla接口的类对象也可以保存在构造器中提供的Comparator对象。
使用优先级 队列的典型示例是任务调度。每一个任务有一个优先级,任务以随机顺序添级最高的任务从队列中删除(由于习惯上加到队列中。每当启动一个新的任务时,都将优先级最高的任务从从队列中删除(由于习惯上将1设为“最高”优先级,所以会将最小的元素删除)。
并不是按照元素的排列顺序访问的。将最程序清单9-5显示了一个正在运行的优先级而删除却总是删掉剩余元素中优先级数级队列。与Tes中的选代不同。这里的选代数最小的那个元素。public static void main(String[] args) { PriorityQueue<LocalDate> queue = new PriorityQueue<>(); queue.add(LocalDate.of(1989,10,12)); queue.add(LocalDate.of(1998,10,12)); queue.add(LocalDate.of(1987,10,12)); queue.add(LocalDate.of(2000,10,12)); queue.forEach( localDate -> { System.out.println(localDate);//元素按小到大输出 }); while (!queue.isEmpty()){ System.out.println(queue.remove());//元素按小到大删除 } }
PriorityQueue(int initCapacity)