1.集合的概念
集合是程序设计中的一种基本思想,在Java中包含数组的概念,由于一维数组有一个固定的概念,所以数组在处理那些随着应用程序的运行而增长或缩减的对象时非常笨拙。
所有的集合父类都是一个Collection的接口。该接口也定义了一些基本的操作。
Collection接口的继承结构如下图所示。
(1)List集合
是线性数据结构的主要体现。List集合的遍历结果是稳定的。该体系最常用的是ArrayList、LinkedList两个类。
ArrayList是容量可以改变的非线程安全的集合。内部实现用数组进行存储,扩容时会创建更大的数组空间,把原有的数据复制到新数组中。支持对元素的快速随机访问,但是插入与删除时速度通常很慢。
LinkedList本质是双向链表。与前者相比,LinkedList插入删除俗得更快,但是随机访问速度很慢。LinkedList包含3个重要的成员:size、first、last。size是双向链表中节点的个数,first和last分别指第一个和最后一个节点的引用。优点:可以将零散的内存单元通过附加引用的方式关联起来,形成按链路顺序查找的线性结构,内存利用率较高。
(2)Queue集合
Queue(队列)是一种先进先出的数据结构。FIFO(先进先出)。
(3)Map集合
Map集合是以Key-Value键值对作为存储元素实现的哈希结构。Key按照某种哈希函数计算后是唯一的,而Value则可以是重复的。
Hashtable因为性能瓶颈已经被淘汰,如今广泛使用的HashMap是线程不安全的。ConcurrentHashMap是线程安全的,在多线程并发场景中推荐使用。
TreeMap是Key有序的Map类集合。
(4)Set集合
Set是不允许出现重复元素的集合类型。Set体系中常用的有HashSet、TreeSet和LInkedHashSet三个类。
HashSet、TreeSet从源码分析分别是使用HashMap、TreeMap实现的。LinkedHashSet继承自HashSet,具有HashSet的优点,内部使用链表维护了元素插入排序。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class 集合 {
public static void main(String[] args) {
List list=new ArrayList();
list.add("ad");
list.add(100);
list.add(1.1f);
Iterator it=list.iterator();
while (it.hasNext()){//遍历全部元素,并打印
Object o=it.next();
System.out.println(o);
}
System.out.println(list.get(1));//得到第一个元素
while (it.hasNext()){//遍历全部元素,并打印特定类型的元素
Object o=it.next();
if(o instanceof String){
System.out.println(o);
}
}
}
}
2. Iterator接口(迭代器、遍历器)
(1)主要用于遍历(迭代访问)Collection中的元素,也称为迭代器。主要用法:
a.boolean hasNext() 如果被迭代的集合元素还没有被遍历,返回true
b.Object next() 返回集合里的下一个元素
c.void remove() 删除集合里上一次next方法返回的元素
3.泛型
使用泛型机制可以避免使用那些杂乱的Object变量,使类型转换的代码具有更好的安全性和可读性。泛型对于集合来说,尤其有用。
泛型和C++语言的模板(template)很相似,使用泛型意味着编写的代码可以被很多不同的类型所重用。
更多遍历方式:
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
System.out.println("--------------");
for(int i :list ){//
System.out.println(i);
}
System.out.println("--------------");
ListIterator<Integer> lit=list.listIterator();
while(lit.hasNext()){
System.out.println(lit.next());
}
while(lit.hasPrevious()){
System.out.println(lit.previous());
}
System.out.println("--------------");
list.forEach(s->System.out.println(s)); //lambda
list.forEach(s->{
System.out.println(s);
System.out.println(s);
}); //lambda
list.forEach(System.out::println);
System.out.println("--------------");
Object os[]=list.toArray();//对象数组
for(int i=0;i<os.length;i++){
System.out.println(os[i]);
}
System.out.println("--------------");
Enumeration<Integer> es=list.elements();
while(es.hasMoreElements()){
System.out.println(es.nextElement());
}
4.Vector和ArrayList的区别
(1)Vcetor类中的所有方法都是线程同步的(synchronized),多个线程共享访问同一个Vector对象将是安全的。
(2)ArrayList类中所有方法都是非同步的,不安全,多线程访问同一个ArrayList对象时,需要自己处理线程同步的问题。
(3)当只有一个线程或者多线程访问时能确保线程安全,使用ArrayList效率高。
5.队列(实现类LinkedList)
java.util.Queue接口,支持队列的常见操作。该接口拓展了java.util.Collection接口。除基本的Collection操作外,队列还提供其他的插入、提取和检查操作。声明格式如下:
public interface Queue<E> extend Collection<E>
(1)FIFO:队列通常以先进先出(FIFO)方式排序各个元素。在FIFO队列中,所有的新元素都插入队列的末尾,移除元素从队列头部移除。
(2)使用Queue时尽量避免使用Collection的add()/remove()方法,而要使用offer()来添加元素,使用poll()
来获取并移除元素。
(3)优点:可以通过返回值判断操作是否成功,add和remove方法在失败时都会抛出异常。
(4)ArrayList、Vector、LinkedList区别:
实现机制不同,ArrayList、Vector内部以数组形式保存集合中元素,因此随机访问集合元素上由较好的性能;而LinkedList内部以链表形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入、删除元素时性能出色。
6.HashTable、HashMap
(1)HashTable
一种高级数据结构,不仅可以像Vector一样动态存储一系列对象,而且为每个被存储的对象(称为value)都用另一个对象(称为key)关联,可以快速的检索数据。
Hashtable是基于哈希表实现的,每个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。Hashtable是线程安全的,能用于多线程环境中。Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。
import java.util.*;
public class Hash {
public static void main(String[] args) {
Hashtable h = new Hashtable();
h.put("A", "Hi");
h.put("B", "!");
h.put("C", 10);
h.put("D", 200);
//遍历方式:
//1
Enumeration es = h.elements();//值Value
while (es.hasMoreElements()) {//遍历,得到的并不是顺序的
System.out.println(es.nextElement());
}
//2
Enumeration ens=h.keys();//键keys
while(ens.hasMoreElements()){
Object o=ens.nextElement();//也可以用泛型写,需将enumeration、hashtable后都加相应泛型
System.out.println(o+":"+h.get(o));
}
System.out.println(h.get("A"));//得到A对应key的值
//3
Collection cs=h.values();
Iterator it=cs.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//4
Set s=h.keySet();
Iterator it1=s.iterator();
while (it1.hasNext()){
Object key=it1.next();
System.out.println(key+" "+h.get(key));
}
//5
Set<Map.Entry> ss=h.entrySet();
for(Map.Entry s1:ss){
System.out.println(s1.getKey()+":"+s1.getValue());
}
//6
h.forEach((k,v)->System.out.println(k+":"+v));
}
}
(2)Hashmap:和Hashtable相比,不安全。
HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。 HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
两者区别:
1.HashTable继承自Dictionary类,HashMap是Map接口的一个实现类(继承自AbstractMap抽象类)。
2.HashMap中,null可以作为键,这样的键只能由一个,可以有一个或多个键所对应的值为null。当get()方法返回null值时,既可以表示没有这个键,也可以表示该键对应的值为空,故不能用来判断HashMap中是否存在某个键,可以用containsKey()方法来判断;而在Hashtable中不允许有null键和值。
3.Hashtable中的方法是同步的,HashMap中的方法在缺省情况下是非同步的。Hashtable比HashMap慢。
4.在两者的方法中,Hashtable仅仅比HashMap多一个elements方法。
5.两者都可以通过entrySet()方法返回一个Set,然后进行遍历处理。Hashtable使用Enumeration,HashMap使用Iterator。
6.Hashtable直接使用对象的HashCode;而HashMap需要重新计算hash值。
7.Hashtable中hash数组默认大小是11,增加公式:old*2+1;HashMap中hash数组默认大小是16,而且一定是2的指数。
(3)HashTable、HashMap排序
借助于Array.sort方法可以对key排序。
可以使用TreeMap类、Collections类对值排序,但必须自己实现排序规则,实现Comparator接口。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class B {
public static void main(String[] args) {
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("A", 100);
map.put("B", 200);
map.put("C", 300);
map.put("D", 400);
map.put("E", 500);
//把Map中的键值对转换到list中,
List<Map.Entry<String, Integer>> list=new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>(){
@Override
public int compare(Entry<String, Integer> o1,
Entry<String, Integer> o2) {
return -(o1.getValue().compareTo(o2.getValue()));
}
});
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}