集合类的作用
集合类也叫做容器类,和数组一样,用于存储数据,但数组类型单一,并且长度固定,限制性很大,而集合类可以动态增长。集合存储的元素都是对象(引用类型),所以集合可以存储不同的数据类型,如果是有序集合,则需要元素类型一致。
集合类支持泛型,避免数据不一致和转换异常。不同的集合类对不同的数据结构进行了封装。所以要熟悉各个集合类的使用场景。
集合框架体系的组成
Collection体系中有三种集合:Set、List、Queue
- Set(集): 元素是无序的且不可重复。
- List(列表):元素是有序的且可重复。
- Queue(队列):封装了数据结构中的队列。
Map体系
- Map用于保存具有映射关系的数据,即key-value(键值对)。Map集合的key是唯一的,不可重复,而value可以重复。所以一个value可以对应多个key。
- Map体系除了常用类之外,还有Properties(属性类)也属于Map体系。
Iterator(迭代器)
- 基本功能就是遍历集合中的所有元素,除了List集合有一个独有的迭代器Listiterator,别的集合类都是一样的。
- 每个集合类都有iterator(),调用该方法就能得到迭代器对象。
- 如果List集合使用迭代器,建议使用ListIterator,比原有的迭代器多了一些方法。
- 除了使用迭代器遍历集合元素,也可以使用加强for和Java8的foreach()方法。如果是List集合,使用for循环也能做到。
Collection接口方法
boolean add(E e)_______________________________添加指定元素 boolean addAll(Collection c)___________________将指定集合中所有元素都添加到此 collection boolean contains(Object o)_____________________集合中是否包含了指定元素 boolean containsAll(Collection<?> c)___________该集合是否包含指定集合中所有元素 boolean remove(Object o)_______________________移除指定元素 boolean removeAll(Collection<?> c)_____________移除指定的collection参数的所有元素 void clear()___________________________________清除集合中所有元素 boolean retainAll(Collection<?> c)_____________保留指定Collection参数中元素,是两个集合的元素交集 boolean equals(Object o)_______________________比较此 collection 与指定对象是否相等 boolean isEmpty()______________________________判断该集合是否为空 Iterator<E> iterator()_________________________返回此 collection 的迭代器,用来遍历元素 int hashCode()_________________________________返回集合的哈希码值 int size()_____________________________________返回此 collection 中的元素数 Object[] toArray()_____________________________返回一个包含该Collection所有元素的对象数组 <T> T[] toArray(T[] a)_________________________返回一个包含该Collection所有元素的数组,数组类型和指定数组的类型一致
Map接口方法
boolean containsKey(Object key)________________该映射是否包含指定键值 boolean containsValue(Object value)____________如果该键值对中有一个或多个key射到指定值,则返回 true Set<Map.Entry<K,V>> entrySet()_________________返回此映射中包含的映射关系的 Set 视图。 Set<K> keySet()________________________________返回此映射中所有键的 Set 视图。 Collection<V> values()_________________________返回一个此映射中包含的值的 Collection V get(Object key)______________________________返回指定键映射的值。如果不存在,则返回 null V put(K key, V value)__________________________放入一个键值对(key-value) void putAll(Map<? extends K,? extends V> m)____把指定映射的所有映射关系复制到此映射中 boolean isEmpty()______________________________如果此映射未包含键值映射关系,则返回 true boolean equals(Object o)_______________________比较指定的对象与此映射是否相等 int size()_____________________________________返回此映射中的键-值映射关系数 int hashCode()_________________________________返回此映射的哈希码值
Set、List、Queue特性和实例
Set(集)
- HashSet:元素是无序的,因为底层操作是HashMap来完成的,所以没有重复值,但可以存入null。
- LinkedHashSet:是HashSet子类,使用链表维护元素次序,元素按照插入次序来排序,其他和HashSet没区别。
- TreeSet:会对元素进行排序,元素都需要实现Comparator接口(Java常用类都已实现),如果是添加自定义的类作为元素,需要制定排序规则。
- TreeSet如果加入不同元素,则元素之间无法比较,所以TreeSet的元素都是同一类型。
- TreeSet增加了许多方法,有获取头尾元素截取一部分元素和使用降序来遍历元素的迭代器,这里不详述。
HashSet的简单示例
HashSet hs = new HashSet(); hs.add("A"); hs.add("潇湘"); hs.add(2); hs.add(2); hs.add(new Date()); Iterator it = hs.iterator(); while(it.hasNext()) { System.out.println(it.next()); } System.out.println("元素个数:" + hs.size()); System.out.println("移除元素:" + hs.remove("A")); hs.clear();//清除所有元素 System.out.println("元素个数:" + hs.size());
- 实例说明:HashSet是Set集合的典型实现,我们要获取Set集合元素除了使用迭代器,还可以使用加强for循环。也可以将集合转成数组。
LinkedHashSet的简单示例
LinkedHashSet hs = new LinkedHashSet(); hs.add("A"); hs.add("潇湘"); hs.add(2); hs.add(new Date()); //使用for-each来遍历元素 for(Object lhs : hs) { System.out.println(lhs); }
- 实例说明:LinkedHashSet和HashSet差不多,不同的就是该类是按照元素的插入次序来排序的。遍历元素可以查看效果。
TreeSet的简单示例
TreeSet ts = new TreeSet(); ts.add(10); ts.add(10); ts.add(30); ts.add(-50); System.out.println(ts);
- 实例说明:使用TreeSet添加多个数值,打印结果是显示有序的。但TreeSet的排序规则不是按照插入次序,而是按照已实现的自然排序来排序。也可以自定义排序。
TreeSet加入不同类型元素的简单示例
public class TreeSetTest { public static void main(String[] args) { TreeSet ts = new TreeSet(); ts.add("A"); ts.add("B"); ts.add("C"); System.out.println(ts.toString()); ts.add(1); System.out.println(ts); //ClassCastException } }
- 实例说明:TreeSet添加不同类型元素,会导致元素之间无法比较,那TreeSet就无法保证排序状态,引发异常。如果使用泛型可以避免数据不一致。
TreeSet新增方法简单示例
TreeSet ts = new TreeSet(); ts.add(10); ts.add(20); ts.add(-50); System.out.println(ts); System.out.println("--------------------"); Iterator it = ts.descendingIterator();//降序迭代器,从大到小 while(it.hasNext()) { System.out.print(it.next() + " "); } //获取>=给定元素的最小元素 System.out.println("ceiling:" + ts.ceiling(18)); System.out.println(ts.first());//第一个元素 System.out.println(ts.last());//最后一个元素 System.out.println(ts.pollFirst());//获取第一位元素,然后移除该元素 System.out.println(ts.pollLast());//获取最后的元素,然后移除该元素 }
- ArrayList:底层是一个可变数组,默认容量10,可以指定初始容量。主要应用在查找和更改。
- LinkedList:底层由链表来维护。主要应用在添加和删除
- ListIterator对Iterator进行了扩展,新增向前迭代、增加元素和修改元素的方法:hasPrevious() 、previous()、add()、set()
List类根据索引新增的方法:
新增的都是根据索引来进行增删改查的方法。
List方法 | 作用描述 |
---|---|
E add(int index, Object obj) |
把指定元素添加到指定索引值对应的元素 。 |
E remove(int index) |
移除指定索引值对应的元素。 |
E set(int index, E element) |
用指定元素替换指定索引值对应的元素。 |
E get(int index) |
返回指定索引值的元素。 |
int indexOf(Object o) |
返回该集合中指定元素第一次出现的索引值;不包含该元素,则返回 -1。 |
int lastIndexOf(Object o) |
返回该集合中指定元素最后一次出现的索引值;不包含该元素,则返回 -1。 |
ListIterator<E> listIterator() |
返回此列表元素的列表迭代器(按适当顺序)。 |
ListIterator<E> listIterator(int index) |
返回列表中元素的列表迭代器(按适当顺序),从指定位置开始。 |
List<E> subList(int fromIndex, int toIndex) |
返回指定的 fromIndex(包括 )和 toIndex(不包括)之间的子集合。 |
ArrayList和LinkedList的简单示例
ArrayList al = new ArrayList(15);//指定初始长度,默认是10 al.add("A"); al.add("B"); al.add(10); al.add(20); System.out.println("移除之前:" + al); al.remove(1); //移除索引值1的对应元素 System.out.println("移除之后:" + al); //获取此时索引值1的对应元素 System.out.println("索引1处的元素:" + al.get(1)); //在索引值1处添加元素 al.add(1,"add"); System.out.println("使用add()在索引1处添加元素:" + al); //修改索引1值的对应元素 al.set(1, "使用set()"); System.out.println("使用set()修改元素:" + al); System.out.println(al.indexOf("使用set()"));//获取元素"使用set()"的索引值 for(int i = 0; i < al.size(); i++) { System.out.println(i + "-->" + al.get(i)); }上面代码中要注意区别add()和set(),add()添加元素至指定索引处,原该索引处的数据依次向后移动,相当于插入元素。set()是对指定索引处的元素进行修改,会覆盖元素。
List特有迭代器—ListIterator的简单示例
ArrayList al = new ArrayList(15); al.add("A"); al.add("B"); al.add(10); al.add(20); System.out.println("----使用ListIterator-----"); ListIterator lis = al.listIterator(); while(lis.hasNext()) { System.out.println(lis.next()); } System.out.println("--------反向迭代---------"); while(lis.hasPrevious()) { System.out.println(lis.previous()); }需要注意:使用hasPrevious()进行反向迭代的前提是先使用一次正向迭代,不然无法迭代元素。
Queue(队列)
- 队列特点:先进先出(FLFO)容器。
- 栈的特点:先进后出(FILO)容器。
- 双端队列特点:拥有栈和队列的特性,可以对队列头部和尾部进行删除和插入操作。
- ArrayDeque:基于数组实现的双端队列。
- PriorityQueue:使用自然排序或定制排序来对元素进行排序,但排序是堆排序。不能插入null。
- LinkedList:实现了List、Queue、Deque接口,既能使用索引,又能模拟双端队列。功能很强大,这里仅做了解。
- ArrayDeque和LinkedList可以作为双端队列,具有FIFO和FILO特性。
双端队列的常用方法
因为实现了Deque和Queue接口,方法众多,但方法中带Frist都是用于操作队头,Last则是操作队尾。建议使用这种方法,简单明了。
boolean add(E e)_____________________________将指定元素插入末尾 void addFirst(E e)__________________________将指定元素插入开头 void addLast(E e)___________________________将指定元素插入末尾 boolean offer(E e)__________________________将指定元素插入末尾 boolean offerFirst(E e)_____________________将指定元素插入末尾 boolean offerLast(E e)______________________将指定元素插入末尾 E getFirst()________________________________获取第一个元素,但不移除 E getLast()_________________________________获取最后一个元素,但不移除 E peekFirst()_______________________________获取双端队列第一个元素,但不移除。 E peekLast()________________________________获取双端队列最后一个元素,但不移除。 E pollFirst()_______________________________获取并移除第一个元素 E pollLast()________________________________获取并移除最后一个元素 E removeFirst()_____________________________获取并移除第一个元素 E removeLast()______________________________获取并移除最后一个元素 E pop()_____________________________________模拟栈弹出一个元素,弹栈 void push(E e)______________________________将元素推入栈中,压栈 boolean removeFirstOccurrence(Object o)_____移除第一次出现的指定元素(当从头部到尾部遍历双端队列时) boolean removeLastOccurrence(Object o)______移除最后一次出现的指定元素(当从头部到尾部遍历双端队列时)
ArrayDeque的简单示例
ArrayDeque ad = new ArrayDeque(); ad.add("A"); ad.add("阴"); ad.add("阳"); ad.add(100); System.out.println(ad); //获取队头元素和队尾 System.out.println("队头元素:" + ad.getFirst()); System.out.println("队尾元素:" + ad.getLast()); //把元素插入队尾 ad.offer("offer加入队尾"); ad.add("add加入队尾"); System.out.println(ad); //移除队列头部元素 ad.removeFirst(); //移除队列最后的元素 ad.removeLast(); System.out.println("移除队头和队尾元素" + ad);
PriorityQueue的简单示例
该类是一个标准队列(FIFO),使用的是堆排序。这意味我们直接打印PriorityQueue对象时,结果可能会不符合我们的要求,它不是会自动排序吗??代码实例如下:PriorityQueue<Integer> al = new PriorityQueue<>(); al.add(18); al.add(30); al.add(-5); al.add(9); al.add(15); System.out.println(al.toString()); //[-5, 9, 18, 30, 15]
- 实例说明:因为堆排序只会保证第一个元素也就是根节点的元素是当前优先队列里最小的,只要元素变化都会导致堆重排,例如使用了offer()添加元素或者poll()获取头部元素。
- 解决方式:使用for循环来遍历元素。注意:加强for循环和迭代器遍历元素都会出现不符合预期的结果。
int len = al.size(); //保证遍历次数是原本的元素数量 for(int i=0; i < len; i++) { System.out.print(al.poll() + " "); //获取元素之后就会移除该元素。 }