Java集合就像一种容器,能够把多个对象(其实是对象的引用,但习惯上都称对象)“丢进”该容器中。从Java 5 增长了泛型之后,Java集合能够记住容器中对象的数据类型,使得编码更加简洁、健壮。html
Java集合大体能够分为两大致系,一个是Collection,另外一个是Mapjava
java.util.Collection下的接口和继承类关系简易结构图:算法
java.util.Map下的接口和继承类关系简易结构图:编程
其中,Java 集合框架中主要封装的是典型的数据结构和算法,如动态数组、双向链表、队列、栈、Set、Map 等。后端
将集合框架挖掘处理,能够分为如下几个部分
1) 数据结构
List
列表、Queue
队列、Deque
双端队列、Set
集合、Map
映射
2) 比较器
Comparator
比较器、Comparable
排序接口
3) 算法
Collections
经常使用算法类、Arrays
静态数组的排序、查找算法
4) 迭代器
Iterator
通用迭代器、ListIterator
针对 List
特化的迭代器数组
List集合的特色就是存取有序,能够存储重复的元素,能够用下标进行元素的操做缓存
List主要实现类:ArrayList、LinkedList、Vector、Stack。安全
ArrayList是一个动态数组结构,支持随机存取,尾部插入删除方便,内部插入删除效率低(由于要移动数组元素);若是内部数组容量不足则自动扩容,所以当数组很大时,效率较低。数据结构
LinkedList是一个双向链表结构,在任意位置插入删除都很方便,可是不支持随机取值,每次都只能从一端开始遍历,直到找到查询的对象,而后返回;不过,它不像 ArrayList 那样须要进行内存拷贝,所以相对来讲效率较高,可是由于存在额外的前驱和后继节点指针,所以占用的内存比 ArrayList 多一些。多线程
Vector也是一个动态数组结构,一个元老级别的类,早在jdk1.1就引入进来类,以后在jdk1.2里引进ArrayList,ArrayList大部分的方法和Vector比较类似,二者是不一样的,Vector是容许同步访问的,Vector中的操做是线程安全的,可是效率低,而ArrayList全部的操做都是异步的,执行效率高,但不安全!
关于Vector
,如今用的不多了,由于里面的get
、set
、add
等方法都加了synchronized
,因此,执行效率会比较低,若是须要在多线程中使用,能够采用下面语句建立ArrayList对象
List<Object> list =Collections.synchronizedList(new ArrayList<Object>());
也能够考虑使用复制容器 java.util.concurrent.CopyOnWriteArrayList
进行操做,例如:
final CopyOnWriteArrayList<Object> cowList = new CopyOnWriteArrayList<String>(Object);
Stack是Vector的一个子类,本质也是一个动态数组结构,不一样的是,它的数据结构是先进后出,取名叫栈!
关于Stack
,如今用的也不多,由于有个ArrayDeque
双端队列,能够替代Stack
全部的功能,而且执行效率比它高!
Set集合的特色:元素不重复,存取无序,无下标;
Set主要实现类:HashSet、LinkedHashSet和TreeSet。
HashSet底层是基于 HashMap 的k
实现的,元素不可重复,特性同 HashMap。
LinkedHashSet底层也是基于 LinkedHashMap 的k
实现的,同样元素不可重复,特性同 LinkedHashMap。
一样的,TreeSet也是基于 TreeMap 的k
实现的,一样元素不可重复,特性同 TreeMap;
Set集合的实现,基本都是基于Map中的键作文章,使用Map中键不能重复、无序的特性;因此,咱们只须要重点关注Map的实现便可!
Queue是一个队列集合,队列一般是指“先进先出”(FIFO)的容器。新元素插入(offer)到队列的尾部,访问元素(poll)操做会返回队列头部的元素。一般,队列不容许随机访问队列中的元素。
Queue主要实现类:ArrayDeque、LinkedList、PriorityQueue。
ArrayQueue是一个基于数组实现的双端队列,能够想象,在队列中存在两个指针,一个指向头部,一个指向尾部,所以它具备“FIFO队列”及“栈”的方法特性。
既然是双端队列,那么既能够先进先出,也能够先进后出,如下是测试例子!
先进先出
public static void main(String[] args) { ArrayDeque<String> queue = new ArrayDeque<>(); //入队 queue.offer("AAA"); queue.offer("BBB"); queue.offer("CCC"); System.out.println(queue); //获取但不出队 System.out.println(queue.peek()); System.out.println(queue); //出队 System.out.println(queue.poll()); System.out.println(queue); }
输出结果:
[AAA, BBB, CCC] AAA [AAA, BBB, CCC] AAA [BBB, CCC]
先进后出
public static void main(String[] args) { ArrayDeque<String> stack = new ArrayDeque<>(); //压栈,此时AAA在最下,CCC在最外 stack.push("AAA"); stack.push("BBB"); stack.push("CCC"); System.out.println(stack); //获取最后添加的元素,但不删除 System.out.println(stack.peek()); System.out.println(stack); //弹出最后添加的元素 System.out.println(stack.pop()); System.out.println(stack); }
输出结果:
[CCC, BBB, AAA] CCC [CCC, BBB, AAA] CCC [BBB, AAA]
LinkedList是List接口的实现类,也是Deque的实现类,底层是一种双向链表的数据结构,在上面我们也有所介绍,LinkedList能够根据索引来获取元素,增长或删除元素的效率较高,若是查找的话须要遍历整合集合,效率较低,LinkedList同时实现了stack、Queue、PriorityQueue的全部功能。
例子
public static void main(String[] args) { LinkedList<String> ll = new LinkedList<>(); //入队 ll.offer("AAA"); //压栈 ll.push("BBB"); //双端的另外一端入队 ll.addFirst("NNN"); ll.forEach(str -> System.out.println("遍历中:" + str)); //获取队头 System.out.println(ll.peekFirst()); //获取队尾 System.out.println(ll.peekLast()); //弹栈 System.out.println(ll.pop()); System.out.println(ll); //双端的后端出列 System.out.println(ll.pollLast()); System.out.println(ll); }
输出结果:
遍历中:NNN 遍历中:BBB 遍历中:AAA NNN AAA NNN [BBB, AAA] AAA [BBB]
PriorityQueue也是一个队列的实现类,此实现类中存储的元素排列并非按照元素添加的顺序进行排列,而是内部会按元素的大小顺序进行排列,是一种可以自动排序的队列。
例子
public static void main(String[] args) { PriorityQueue<Integer> queue1 = new PriorityQueue<>(10); System.out.println("处理前的数据"); Random rand = new Random(); for (int i = 0; i < 10; i++) { Integer num = rand.nextInt(90) + 10; System.out.print(num + ", "); queue1.offer(num); // 随机两位数 } System.out.println("\n处理后的数据"); for (int i = 0; i < 10; i++) { // 默认是天然排序 [升序] System.out.print(queue1.poll() + ", "); } }
输出结果:
处理前的数据 36, 23, 24, 11, 12, 26, 79, 96, 14, 73, 处理后的数据 11, 12, 14, 23, 24, 26, 36, 73, 79, 96,
Map是一个双列集合,其中保存的是键值对,键要求保持惟一性,值能够重复。
Map 主要实现类:HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties。
关于HashMap,相信你们都不陌生,继承自AbstractMap,key 不可重复,由于使用的是哈希表存储元素,因此输入的数据与输出的数据,顺序基本不一致,另外,HashMap最多只容许一条记录的 key 为 null。
HashMap 的子类,内部使用链表数据结构来记录插入的顺序,使得输入的记录顺序和输出的记录顺序是相同的。LinkedHashMap与HashMap最大的不一样处在于,LinkedHashMap输入的记录和输出的记录顺序是相同的!
可以把它保存的记录根据键排序,默认是按键值的升序排序,也能够指定排序的比较器,当用 Iterator 遍历时,获得的记录是排过序的;如需使用排序的映射,建议使用 TreeMap。TreeMap实际使用的比较少!
继承自AbstractMap,与HashMap有些不一样,在获取元素的时候,经过==
代替equals ()
来进行判断,比较的是内存地址。
get方法源码部分
public V get(Object key) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); while (true) { Object item = tab[i]; //用==比较k和元素是否相等 if (item == k) return (V) tab[i + 1]; if (item == null) return null; i = nextKeyIndex(i, len); } }
WeakHashMap继承自AbstractMap,被称为缓存Map,向WeakHashMap中添加元素,再次经过键调用方法获取元素方法时,不必定获取到元素值,由于WeakHashMap 中的 Entry 可能随时被 GC 回收。
Hashtable,一个元老级的类,键值不能为空,与HashMap不一样的是,方法都加了synchronized
同步锁,是线程安全的,可是效率上,没有HashMap快!
同时,HashMap 是 HashTable 的轻量级实现,他们都完成了Map 接口,区别在于 HashMap 容许K和V为空,而HashTable不容许K和V为空,因为非线程安全,效率上可能高于 Hashtable。
若是须要在多线程环境下使用HashMap,可使用以下的同步器来实现或者使用并发工具包中的ConcurrentHashMap
类
Map<String, Object> map =Collections.synchronizedMap(new HashMap<>());
Properties继承自HashTable,Properties新增了load()和和store()方法,能够直接导入或者将映射写入文件,另外,Properties的键和值都是String类型。
Comparable和Comparator接口都是用来比较大小的,通常在TreeSet、TreeMap接口中使用的比较多,主要用于解决排序问题。
Comparable:对实现它的每一个类的对象进行总体排序
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }
若一个类实现了Comparable 接口,实现 Comparable 接口的类的对象的 List 列表 ( 或数组)能够经过 Collections.sort(或 Arrays.sort)进行排序。
此外,实现 Comparable 接口的类的对象 能够用做 “有序映射 ( 如 TreeMap)” 中的键或 “有序集合 (TreeSet)” 中的元素,而不须要指定比较器。
使用例子:
/** * 实体类Person实现Comparable接口 */ public class Person implements Comparable<Person>{ private int age; private String name; public Person(String name, int age){ this.name = name; this.age = age; } @Override public int compareTo(Person o){ return this.age-o.age; } @Override public String toString(){ return name+":"+age; } }
测试
public static void main(String[] args) { Person person1 = new Person("张三",18); Person person2 = new Person("李四",17); Person person3 = new Person("王五",19); List<Person> list = new ArrayList<>(); list.add(person1); list.add(person2); list.add(person3); System.out.println(list); Collections.sort(list); System.out.println(list); }
输出:
[张三:18, 李四:17, 王五:19] [李四:17, 张三:18, 王五:19]
Comparator:也是对实现它的每一个类的对象进行排序
package java.util; import ***; public interface Comparator<T> { int compare(T o1, T o2); ...... }
若是咱们的这个类Person
没法修改或者没有继承Comparable
接口,咱们又要对其进行排序,Comparator就能够派上用场了。
将类Person
实现的Comparable
接口去掉
/** * 实体类Person */ public class Person { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age){ this.name = name; this.age = age; } @Override public String toString(){ return name+":"+age; } }
测试类:
public static void main(String[] args) { Person person1 = new Person("张三",18); Person person2 = new Person("李四",17); Person person3 = new Person("王五",19); List<Person> list = new ArrayList<>(); list.add(person1); list.add(person2); list.add(person3); System.out.println(list); Collections.sort(list, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { if(o1 == null || o2 == null){ return 0; } //o1比o2小,返回负数 //o1等于o2,等于0 //o1大于o2,返回正数 return o1.getAge()-o2.getAge(); } }); System.out.println(list); }
输出:
[张三:18, 李四:17, 王五:19] [李四:17, 张三:18, 王五:19]
java.util.Collections工具类为集合框架提供了不少有用的方法,这些方法都是静态的,在编程中能够直接调用。整个Collections工具类源码差很少有4000行,这里只针对一些典型的方法进行阐述。
addAll:向指定的集合c中加入特定的一些元素elements
public static <T> boolean addAll(Collection<? super T> c, T… elements)
binarySearch:利用二分法在指定的集合中查找元素
#集合元素T实现Comparable接口的方式,进行查询 public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) #元素之外部实现Comparator接口的方式,进行查询 public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
#集合元素T实现Comparable接口的方式,进行排序 public static <T extends Comparable<? super T>> void sort(List<T> list) #元素之外部实现Comparator接口的方式,进行排序 public static <T> void sort(List<T> list, Comparator<? super T> c)
shuffle:混排,随机打乱原来的顺序,它打乱在一个List中可能有的任何排列的踪影。
#方法一 public static void shuffle(List<?> list) #方法二,指定随机数访问 public static void shuffle(List<?> list, Random rnd)
reverse:集合排列反转
#直接反转集合的元素 public static void reverse(List<?> list) #返回可使集合反转的比较器Comparator public static <T> Comparator<T> reverseOrder() #集合的反转的反转,若是cmp不为null,返回cmp的反转的比较器,若是cmp为null,效果等同于第二个方法. public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
synchronized系列:确保所封装的集合线程安全(强同步)
#同步Collection接口下的实现类 public static <T> Collection<T> synchronizedCollection(Collection<T> c) #同步SortedSet接口下的实现类 public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s) #同步List接口下的实现类 public static <T> List<T> synchronizedList(List<T> list) #同步Map接口下的实现类 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) #同步SortedMap接口下的实现类 public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
java.util.Arrays工具类也为集合框架提供了不少有用的方法,这些方法都是静态的,在编程中能够直接调用。整个Arrays工具类源码有3000多行,这里只针对一些典型的方法进行阐述。
asList:将一个数组转变成一个List,准确来讲是ArrayList
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
注意:这个List是定长的,企图添加或者删除数据都会报错java.lang.UnsupportedOperationException
sort:对数组进行排序,适合byte,char,double,float,int,long,short等基本类型,还有Object类型
#基本数据类型,例子int类型数组 public static void sort(int[] a) #Object类型数组 #若是使用Comparable进行排序,Object须要实现Comparable #若是使用Comparator进行排序,可使用外部比较方法实现 public static void sort(Object[] a)
binarySearch:经过二分查找法对已排序的数组进行查找。若是数组没有通过Arrays.sort排序,那么检索结果未知。
适合byte,char,double,float,int,long,short等基本类型,还有Object类型和泛型。
#基本数据类型,例子int类型数组,key为要查询的参数 public static int binarySearch(int[] a, int key) #Object类型数组,key为要查询的参数 #若是使用Comparable进行排序,Object须要实现Comparable #若是使用Comparator进行排序,可使用外部比较方法实现 public static int binarySearch(Object[] a, Object key)
copyOf:数组拷贝,底层采用System.arrayCopy(native方法)实现。
适合byte,char,double,float,int,long,short等基本类型,还有泛型数组。
#基本数据类型,例子int类型数组,newLength新数组长度 public static int[] copyOf(int[] original, int newLength) #T为泛型数组,newLength新数组长度 public static <T> T[] copyOf(T[] original, int newLength)
copyOfRange:数组拷贝,指定必定的范围,底层采用System.arrayCopy(native方法)实现。
适合byte,char,double,float,int,long,short等基本类型,还有泛型数组。
#基本数据类型,例子int类型数组,from:开始位置,to:结束位置 public static int[] copyOfRange(int[] original, int from, int to) #T为泛型数组,from:开始位置,to:结束位置 public static <T> T[] copyOfRange(T[] original, int from, int to)
equals:判断两个数组的每个对应的元素是否相等(equals, 对于两个数组的元素a和a2有a==null ? a2==null : a.equals(a2))
#基本数据类型,例子int类型数组,a为原数组,a2为目标数组 public static boolean equals(int[] a, int[] a2) #Object数组,a为原数组,a2为目标数组 public static boolean equals(Object[] a, Object[] a2)
deepEquals:主要针对一个数组中的元素仍是数组的状况(多维数组比较)
#Object数组,a1为原数组,a2为目标数组 public static boolean deepEquals(Object[] a1, Object[] a2)
toString:将数组转换成字符串,中间用逗号隔开
#基本数据类型,例子int类型数组,a为数组 public static String toString(int[] a) #Object数组,a为数组 public static String toString(Object[] a)
deepToString:当数组中又包含数组,就不能单纯的利用Arrays.toString()了,使用此方法将数组转换成字符串
#Object数组,a为数组 public static String deepToString(Object[] a)
JCF的迭代器(Iterator)为咱们提供了遍历容器中元素的方法。只有容器自己清楚容器里元素的组织方式,所以迭代器只能经过容器自己获得。每一个容器都会经过内部类的形式实现本身的迭代器。
ArrayList<String> list = new ArrayList<String>(); list.add(new String("a1")); list.add(new String("a2")); list.add(new String("a3")); Iterator<String> it = list.iterator();//获得迭代器 while(it.hasNext()){ String obj = it.next();//访问元素 System.out.println(obj); }
JDK 1.5 引入了加强的for循环,简化了迭代容器时的写法
//使用加强for迭代 ArrayList<String> list = new ArrayList<String>(); list.add(new String("a1")); list.add(new String("a2")); list.add(new String("a3")); for(String obj : list){ //enhanced for statement System.out.println(obj); }
以上,主要是对java集合的总体架构进行简单的介绍,若是有理解不当之处,欢迎指正。
一、JDK1.7&JDK1.8 源码
二、Otokaze's Blog - Java Collection框架
三、CSDN - 朱小厮 - Comparable与Comparator浅析
做者:炸鸡可乐
原文出处:www.pzblog.cn