Collection集合框架整理

在讲Collection集合框架之前呢,先来了解下数据结构
在面向过程的C语言中,数据库结构用struct来描述,而在面向对象的编程中,数据结构是用类来描述的,并且包含有对该数据结构操作的方法。
在Java语言中,Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类)。所有抽象出来的数据结构和操作(算法)统称为Java集合框架(JavaCollectionFramework)。
常见的结构有哪些呢?
1.数组
按照顺序的结构存储数据,两个相邻的数据地址是挨着的
特点:
1.查找和修改数据的速度快,效率高;
2.增删数据的速度慢,效率低;
2.链表
存储数据不一定要一段连续的内存空间,但在每个数据的内存空间中都存储了下一个数据的地址。(想要查找一个元素得先找到前一个元素,得到下一个元素的地址才能获取)
特点:
1.增删数据的速度快,效率高;
2.查找数据的速度慢,效率低;
3.哈希表,散列表(hash)
主要使用数组来储存数据,结构复杂。根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
特点:
1.如果里面使用纯数组存储数据,它是查找数据最快的算法;
2.可能会浪费内存空间;

4.队列
先进先出,后进后出
5.栈
先进后出!

=======================================
其实Java程序员在具体应用时,不必考虑数据结构和算法实现细节,只需要用这些类创建出来一些对象,然后直接应用就可以了,这样就大大提高了编程效率。(上边的了解下就行~=-=)

好了,数据结构大体了解完是怎么回事了,我们就开始今天的正文吧!!!

首先来看结构图:
这里写图片描述
Collection接口是Set、List和Queue接口的父接口,基本操作包括:

add(Object o):增加元素
addAll(Collection c):一次性增加所有元素
clear():清空
contains(Object o):是否包含指定元素
containsAll(Collection c):是否包含集合c中的所有元素
iterator():返回Iterator对象,用于遍历集合中的元素
remove(Object o):移除元素
removeAll(Collection c):相当于减集合c
retainAll(Collection c):相当于求与c的交集
size():返回元素个数
toArray():把集合转换为一个数组

从Collection结构图中发现,这是个庞大的体系,那么对于如何遍历出实现类中的元素就是一个问题了,如果每一个实现类都有自己的方式,那就太不方便了,所以在Collection接口中给我们提供了一个叫迭代器的玩意(iterator)=-=!
Collection的遍历可以使用Iterator接口或者是foreach循环来实现

Iteratorit=collection.iterator();//获得一个迭代子
while(it.hasNext()){
Objectobj=it.next();//得到下一个元素
}

注意:推荐能用用for循环就用for循环,因为用iterator可能引发并发修改异常

======================================

下面从List子接口开始
List子接口是有序集合,(Set集合是不保证恒久有序的)增加了与索引位置相关的操作:

add(int index, Object o):在指定位置插入元素
addAll(int index, Collection c):...
get(int index):取得指定位置元素
indexOf(Obejct o):返回对象o在集合中第一次出现的位置
lastIndexOf(Object o):...
remove(int index):删除并返回指定位置的元素
set(int index, Object o):替换指定位置元素
subList(int fromIndex, int endIndex):返回子集合

然后是ArrayList和Vector还有LinkedList实现类

这三个类都是基于数组实现的List类。
LinkedList,ArrayList是线程不安全的,而Vector是线程安全的。但Vector的性能会比ArrayList低,且考虑到兼容性的原因,有很多重复方法。
Vector提供一个子类Stack,可以挺方便的模拟“栈”这种数据结构(后进先出)。
ArrayList里面使用长度可变的数组存储数据.
特点:
1.查找和修改数据的速度快;
2.增删数据的速度慢;
LinkedList里面使用链表存储数据.
特点:
1.查找数据的速度慢;
2.增删数据的速度快;
二者相比,LinkedList增加了一批操作头尾节点的方法,操作头尾节点的方法:addFirst(E e):把数据添加在第一个节点的位置,原来的第一个节点就变成第二个节点.(一堆操作头尾的方法,用法相同,就不一一例举了)

在java.util.concurrent包中定义的CopyOnWriteArrayList提供了线程安全的Arraylist,但是当进行add和set等变化操作时它是通过为底层数组创建新的副本实现的,所以比较耗费资源

(源码:publicboolean add(E e) {

finalReentrantLock lock =this.lock;

lock.lock();

try {

Object[] elements = getArray();

int len = elements.length;

Object[] newElements = Arrays.copyOf(elements,len + 1);

newElements[len] = e;

setArray(newElements);

return true;

} finally {

lock.unlock();

}

})
但是如果存在频繁遍历,写入和修改操作多的时候这种遍历就相对于自己进行的同步遍历效果要好,而且它也允许存在null元素)

Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。通过使用capacity和ensurecapacity操作以及capacityIncrement域可以优化存储操作,(Vector的Iterator和listIterator方法翻译的迭代器支持fail-fast机制,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。官方对此的说明是 java.util 包中的集合类都返回 fail-fast迭代器,这意味着它们假设线程在集合内容中进行迭代时,集合不会更改它的内容。如果 fail-fast迭代器检测到在迭代过程中进行了更改操作,那么它会抛出ConcurrentModificationException,这是不可控异常。
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。)
注意:是不是看蒙了!这个Vector,Stack看看就好,哈哈。早就过时的老掉牙了,不推荐使用Vector类,即使需要考虑同步,即也可以通过其它方法实现(例如:可以加锁吗)。同样我们也可以通过ArrayDeque类或LinkedList类实现“栈”的相关功能。所以Vector与子类Stack,建议放进大脑的历史收藏区吧

不像ArrayList是基于数组实现的线性表,LinkedList类是基于链表实现的。

另外还有固定长度的List:Arrays工具类的方法asList(Object… a)可以将数组转换为List集合,它是Arrays内部类ArrayList的实例,特点是不可以增加元素,也不可以删除元素。
下一步Set子接口

Set集合不允许包含相同的元素,而判断两个对象是否相同则是根据equals方法。

HashSet类是Set接口的典型实现类。

特点:
1.不能保证元素的排列顺序,加入的元素要特别注意hashCode()方法的实现。
2.HashSet不是同步的,多线程访问同一步HashSet对象时,需要手工同步。
3.集合元素值可以是null。

LinkedHashSet类

LinkedHashSet类也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序。与HashSet相比,特点:

对集合迭代时,按增加顺序返回元素。
性能略低于HashSet,因为需要维护元素的插入顺序。但迭代访问元素时会有好性能,因为它采用链表维护内部顺序。

SortedSet接口及TreeSet实现类

TreeSet类是SortedSet接口的实现类。因为需要排序,所以性能肯定差于HashSet。与HashSet相比,额外增加的方法有:

first():返回第一个元素
last():返回最后一个元素
lower(Object o):返回指定元素之前的元素
higher(Obect o):返回指定元素之后的元素
subSet(fromElement, toElement):返回子集合

可以定义比较器(Comparator)来实现自定义的排序。默认自然升序排序。

EnumSet类

EnumSet类是专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值。

import java.util.*;

public class Text {
    public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
    public void applyStyles(Set<Style> styles) {

    }
    public static void main(String[] args) {
        Text text = new Text();
        text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
    }
}

在一个就是 Queue子接口
Queue用于模拟队列这种数据结构,实现“FIFO”等数据结构。通常,队列不允许随机访问队列中的元素。

Queue 接口并未定义阻塞队列的方法,而这在并发编程中是很常见的。BlockingQueue 接口定义了那些等待元素出现或等待队列中有可用空间的方法,这些方法扩展了此接口。

Queue 实现通常不允许插入 null 元素,尽管某些实现(如 LinkedList)并不禁止插入 null。即使在允许 null 的实现中,也不应该将 null 插入到 Queue 中,因为 null 也用作 poll 方法的一个特殊返回值,表明队列不包含元素。

基本操作:

boolean add(E e) : 将元素加入到队尾,不建议使用
boolean offer(E e): 将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。推荐使用此方法取代add
E remove(): 获取头部元素并且删除元素,不建议使用
E poll(): 获取头部元素并且删除元素,队列为空返回null;推荐使用此方法取代remove
E element(): 获取但是不移除此队列的头
E peek(): 获取队列头部元素却不删除元素,队列为空返回null

其中的类有如下:
PriorityQueue类

PriorityQueue保存队列元素的顺序并不是按照加入队列的顺序,而是按队列元素的大小重新排序。当调用peek()或者是poll()方法时,返回的是队列中最小的元素。当然你可以与TreeSet一样,可以自定义排序。

Deque子接口与ArrayDeque类

Deque代表一个双端队列,可以当作一个双端队列使用,也可以当作“栈”来使用,因为它包含出栈pop()与入栈push()方法。

ArrayDeque类为Deque的实现类,数组方式实现。方法有:

addFirst(Object o):元素增加至队列开头
addLast(Object o):元素增加至队列末尾
poolFirst():获取并删除队列第一个元素,队列为空返回null
poolLast():获取并删除队列最后一个元素,队列为空返回null
pop():“栈”方法,出栈,相当于removeFirst()
push(Object o):“栈”方法,入栈,相当于addFirst()
removeFirst():获取并删除队列第一个元素
removeLast():获取并删除队列最后一个元素

实现List接口与Deque接口的LinkedList类

LinkedList类是List接口的实现类,同时它也实现了Deque接口。因此它也可以当做一个双端队列来用,也可以当作“栈”来使用。并且,它是以链表的形式来实现的,这样的结果是它的随机访问集合中的元素时性能较差,但插入与删除操作性能非常出色。

继续,咱们一块讲一下Map集合
java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap

Map主要用于存储健值对,根据键得到值,因此不允许键重复,但允许值重复。

Hashmap 是一个 最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力.

Hashtable 与HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。

LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历 的时候会比HashMap慢。

TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。

附上map集合的四种遍历方式代码

public static void main(String[] args) {


  Map<String, String> map = new HashMap<String, String>();
  map.put("1", "value1");
  map.put("2", "value2");
  map.put("3", "value3");

  //第一种:普遍使用,二次取值
  System.out.println("通过Map.keySet遍历key和value:");
  for (String key : map.keySet()) {
   System.out.println("key= "+ key + " and value= " + map.get(key));
  }

  //第二种
  System.out.println("通过Map.entrySet使用iterator遍历key和value:");
  Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
  while (it.hasNext()) {
   Map.Entry<String, String> entry = it.next();
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }

  //第三种:推荐,尤其是容量大时
  System.out.println("通过Map.entrySet遍历key和value");
  for (Map.Entry<String, String> entry : map.entrySet()) {
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }

  //第四种
  System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
  for (String v : map.values()) {
   System.out.println("value= " + v);
  }
 }

总结

*ListSetMap将持有对象一律视为Object型别。
*Collection、ListSetMap都是接口,不能实例化。
继承自它们的 ArrayList, Vector, HashTable, HashMap是具象class,这些才可被实例化。
*vector容器确切知道它所持有的对象隶属什么型别。vector不进行边界检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值