在了解集合最初,我们必须要了解它的含义。
集合类是Java数据结构的实现。Java的集合类是java.util包中的重要内容,它允许以各种方式将元素分组,并定义了各种使这些元素更容易操作的方法。Java集合类是Java将一些基本的和使用频率极高的基础类进行封装和增强后再以一个类的形式提供。集合类是可以往里面保存多个对象的类,存放的是对象,不同的集合类有不同的功能和特点,适合不同的场合,用以解决一些实际问题。
首先,在最前面来个图,这个图十分经典(偶然间看到的),实线边框的是实现类,折线边框的是抽象类,而点线边框的是接口。带有空心箭头的点线表示一个特定的类实现了一个接口,实心箭头表示某个类可以生成箭头所指向类的对象
这个就是java集合的一个初步的关系,其中Iterator是迭代器,是java为各种容器提供的公共的操作接口。这样使得对容器的遍历操作与其具体的底层实现相隔离,达到解耦的效果。接下来我们将逐一认识他们。
首先,java集合分两大类,一类为实现Collection接口,另一类为实现Map接口。这就衍生出两类集合派别。
Collection
它是一个线性,存放的是value
Map
它是一组键值对的对象,存放的是key:value
Iterator
前面提到了Iterator是java为各种容器提供的公共的操作接口,俗称迭代器,是集合的专用遍历方式。构造方法:Iterator<E> iterator()
返回此集合中元素的迭代器
迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
迭代器中的常用方法
E next():返回迭代中的下一个元素
boolean hasNext(),如果迭代具有更多元素,则返回true
void remove():从迭代器中指向的集合中移除迭代器返回的最后一个元素(可选操作)
Collection接口是单例集合的顶层接口,JDK不提供此类的任何直接实现。它下面还有三个子接口,一个为可重复的List接口,另一个为不可重复的Set接口,还有一个为Queue。
List接口
- 有序:存储和取出的元素顺序一致
- 可重复:存储的元素可以重复
List接口有两个实现类,一个为ArrayList类,另一个为LinkedList类
ArratList底层用的数据结构是数组,查询快,增删慢
ArrayList构造方法:
public ArrayList():创建一个空的集合对象
ArrayList添加方法:
public boolean add(E e):将指定的元素追加到此集合的末尾
public void add(int index,E element):在此集合中的指定位置插入指定的元素
ArrayList常用方法:
public boolean remove(Object o):删除指定的元素,返回删除是否成功
public E remove(int index):删除指定索引处的元素,返回被删除的元素
public E set(int index,E element):修改指定索引处的元素,返回被修改的元素
public E get(int index):返回指定索引处的元素
public int size():返回集合中的元素的个数
LinkedList底层用的数据结构是链表,增删快,查询慢
LinkedList集合的特有功能:
public void addFirst(E e):在该列表开头插入指定的元素
public void addLast(E e):将指定的元素追加到此列表的末尾
public E getFirst():返回此列表中的第一个元素
public E getLast():返回此列表中的最后一个元素
public E removeFirst():从此列表中删除并返回第一个元素
public E removeLast():从此列表中删除并返回最后一个元素
List还有一个特有的列表迭代器——ListIterator
通过List集合的ListIterator()方法得到,所以说它是List集合特有的迭代器。用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。
ListIterator()中的常用方法:
E next():返回迭代中的下一个元素
boolean hasNext():如果迭代具有更多元素,则返回true
E previous():返回列表中的上一个元素
boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
void add(E e):将指定的元素插入列表
实例:
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
list.add("java");
//列表迭代器搭建一次就行了
ListIterator<String> lit = list.listIterator();
while(lit.hasNext()){
String s = lit.next();
System.out.println(s);
}
while(lit.hasPrevious()){
String s1 = lit.previous();
}
//在这里并不会出现并发异常,因为在ListIterator接口下的实现类有个方法是把实际修改集合次数赋值给预期修改次数
//如果这里是Iterator,则会产生并发异常,因为Iterator下的next方法会把实际修改次数与预期修改次数对比,不相等则抛出一个异常
ListIterator<String> lits = list.listIterator();
while (lits.hasNext()){
String s = lits.next();
if (s.equals("world")){
lits.add(("Javaee"));
}
}
Set接口
- 不包含重复元素的集合
- 没有带索引的方法,所以不能使用普通for循环遍历,因为不能用set.get()获取元素。
Set接口有两个实现类,一个为HashSet类,另一个为TreeSet类
HashSet
- 底层数据结构是哈希表
- 对集合的迭代顺序不做任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,因此不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
同时,LinkedHashSet继承了HashSet,源码更少、更简单,唯一的区别是内部使用的是LinkedHashMap。
- 哈希表和链表实现的Set接口,具有可预测的迭代顺序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 有哈希表保证元素唯一,也就是说没有重复的元素
这里解释一下Set集合保证元素唯一性的方法
①你存入一个元素,会根据元素的哈希值计算对象的存储位置,如果该位置没有对象,则存入元素
②如果该位置有元素,则遍历该位置的所有元素,将存入的元素会和以前的元素比较哈希值 如果哈希值不同,会继续往下执行,把元素添加到集合
③如果哈希值相同,会调用对象的equals()方法比较
如果返回false,会继续向下执行,把元素添加到集合
如果返回true,说明元素重复,不存储
TreeSet
- 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator):根据指定的比较器进行排序 - 没有带索引的方法,所以不能使用普通的for循环遍历
- 由于是set集合,所以不包括重复元素的集合
Queue
Queue也是Collection的子接口。不过通常情况下,LinkedList可以被用作Queue的一种实现,因为它实现了Queue的接口。不过还有一个类,叫PriorityQueue,它是一个比较标准的队列实现类,但不是绝对标准。因为PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此当调用peek()、pull()方法来取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。这其实违背了先进先出(FIFO)规则。
Map接口是键值对集合的顶层接口,JDK也不提供此类的任何直接实现。
它的结构为Interface Map<K,V> K:键的类型 V:值的类型
。Map集合将键映射到值的对象,同时不能包含重复的键,每个键可以映射到最多一个值。它下面还有两个子接口,一个为AbstractMap接口,另一个为SortedMap接口。其中他们的实现类中比较常用的就是HashMap,TreeMap了。
HashMap
HashMap是我们使用最多的一个Map具体类。它是根据键的HashCode值来存储数据,根据键可以直接获取它的值,访问速度极快,但数据的存储是无序的。并且HashMap允许key和value都为null。
HashMap基本方法:
V put(K key,V value):添加元素
V remove(Object key):根据键删除键值对元素,返回键所对应的值
void clear():移除所有的键值对元素
boolean containsKey(Object value):判断集合是否包含指定的键
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty():判断集合是否为空
int size():集合的长度,也就是集合中键值对的个数
V get(Object key):根据键获取值
Set<K> keySet():获取所有键的集合
Collection<V> values():获取所有值的集合
Map集合遍历有两种方式,
- 方式一:
1、获取所有键的集合,用keyset()实现;
2、遍历键的集合,获取到每一个键
3、根据键去找值,用get(Object key方法实现) - 方式二:
1、获取所有键值对对象的集合
Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
2、遍历键值对对象的集合,得到每一个键值对对象
3、根据键值对对象获取键和值。用getKey()得到键,用getValue()得到值
实例
Map<String, String> map = new HashMap<String, String>();
map.put("a0001", "pizssn");
map.put("a0002", "yettaa");
map.put("a0003", "piyiyi");
map.put("a0004", "johnnn");
//第一种方式
Set<String> set = map.keySet();
for (String s : set) {
System.out.println(s + "---" + map.get(s));
}
//第二种方式
Set<Map.Entry<String, String>> sm = map.entrySet();
for (Map.Entry<String, String> m : sm) {
System.out.println(m.getKey() + "---" + m.getValue());
}
当然HashMap知识点远不止这些,没讲的会放在另外一个博客去扩展。
LinkedHashMap继承自HashMap,它相较于HashMap,最大的优点就是可以实现迭代有序,这个有序可以是插入顺序或者访问顺序。作为牺牲,它增加了时间和空间上的开销。因为它是通过维护一个运行于所有条目的双向链表,来实现元素迭代有序。Key和Value也都允许为null。
TreeMap
TreeMap实现了SortMap接口,因此TreeMap是Key有序的。它默认的排序规则是升序排序,当然开发者也可以指定排序比较器,修改排序规则。TreeMap是基于红黑树实现的,其默认的节点颜色是黑色。当然要注意的一点是插入的键值不能为null。
Collections类
很多人都会把Collection和Collections弄混,Collection是单例集合的顶层接口,而Collections是针对集合操作的工具类。
Collections类的常用方法:
public static <T extends Comparable<? super T>> void sort(List<T> list):将指定的列表
public static void reverse(List<?> list):反转指定元素的顺序
public static void shuffle(List<?> list):使用默认源随机排列指定的列表