疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java的集合框架体系介绍


 

 
集合类的作用
集合类也叫做容器类,和数组一样,用于存储数据,但数组类型单一,并且长度固定,限制性很大,而集合类可以动态增加长度。
集合存储的元素都是对象(引用地址),所以集合可以存储不同的数据类型,但如果是需要比较元素来排序的集合,则需要类型一致。
集合中提供了统一的增删改查方法,使用方便。
支持泛型,避免数据不一致和转换异常,还对常用的数据结构进行了封装。
集合框架体系的组成
集合框架体系是由Collection、Map(映射关系)和Iterator(迭代器)组成,各部分的作用如下所示。
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接口方法
Collection是Set、List和Queue接口的父接口,Set、List、Queue的子类都能使用如下方法。
 
 
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(集)
特点:Set集合存储元素的特性是无序且不可重复的。还有Set是基于map实现的,详情可看Set实现类的源码。
Set集合的大部分方法和Collection接口差不多,只有TreeSet新增了较多方法。
主要实现类:
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());//获取最后的元素,然后移除该元素
}
特点:List集合的元素是有序的且可重复。每个元素都有对应的索引,默认按照添加的顺序设置索引。
主要实现类:
ArrayList:底层是一个可变数组,默认容量10,可以指定初始容量。主要应用在查找和更改。
LinkedList:底层由链表来维护。主要应用在添加和删除
List的独有迭代器:ListIterator
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与LinkedList的主要差别在底层实现方式不一样,但方法差不多。所以这里只使用ArrayList来演示索引方法
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(队列)
Queue集合封装了栈、队列和双端队列数据结构,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() + " "); //获取元素之后就会移除该元素。
}