引言

  以下是介绍Java有关集合类,以及对应每个类的用途,同时进行比较集合类的不同特点来让我们深入了解。

Collction接口

  • Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。
  • 如何遍历Collction中的每一个元素?使用迭代器iterator,参考下面代码
 Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}

List接口

  • List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
  • 除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。
  • 实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkList类

  • LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  • 除了有ArrayList的基本操作方法外,还提供了get、remove、insert方法,LinkList不能随机访问
  • 注意LinkedList没有同步性。如果多个线程同时访问一个LinkedList,则必须自己实现访问同步。另一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类

  • ArrayList实现了大小可变的数组。它允许所有元素,包括null。ArrayList没有同步性。
  • size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  • 每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  • 遍历方法:for、foreach、iterator、listiterator

Vector类

  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了 Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。

Stack类

  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的pushpop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Vector、ArrayList和LinkedList比较

  1. Vector是线程同步的,所以它也是线程安全的,而ArrayList和LinkedList是非线程安全的。如果不考虑到线程的安全因素,一般用ArrayList和LinkedList效率比较高。
  2. ArrayList和Vector是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
  3. 如果集合中的元素的数目大于目前集合数组的长度时,Vector增长率为目前数组长度的100%,而ArrayList增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势;反之,用ArrayList有优势。
  4. 如果查找一个指定位置的数据,Vector和ArrayList使用的时间是相同的,花费时间为O(1),而LinkedList需要遍历查找,花费时间为O(i),效率不如前两者。
  5. 而如果移动、删除一个指定位置的数据花费的时间为0(n-i)n为总长度,这个时候就应该考虑使用LinkedList,因为它移动一个指定位置的数据所花费的时间为0(1)。
  6. 对于在指定位置插入数据,LinedList比较占优势,因为ArrayList要移动数据。

Set类

  • Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  • 很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。

HashSet类

  HashSet 是一个没有重复元素的集合。它是由HashMap实现的不保证元素的顺序。(元素插入的顺序和输出的顺序不一致)

  HashSet 允许使用null元素,是非同步的。

  HashSet使用和理解中容易出现的误区:

    a.HashSet中存放null值:HashSet中是允许存入null值的,但是在HashSet中仅仅能够存入一个null值。

    b.HashSet中存储元素的位置是固定的:HashSet中存储的元素的是无序的,这个没什么好说的,但是由于HashSet底层是基于Hash算法实现的,使用了hashcode,所以HashSet中相应的元素的位置是固定的

    c.必须小心操作可变对象(Mutable Object),如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

LinkedHashSet类

  LinkHashSet继承自HashSet,其底层是基于LinkedHashMap来实现的,有序,非同步。元素以插入的顺序来保存数据。

Map类

  Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。

  Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hastable类

  • Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value
  • 添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
  • Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
  • 由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object。
  • Hashtable是同步的,线程安全的。

HashMap类

  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap 的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。

  HashMap的底层是通过“数组+链表”实现的,而在JDK1.8后,则是“数组+链表+红黑树”实现的。

  ConcurrentHashMap

  对于HashMap,最主要的是以下四种的操作:put、get、remove、迭代

    put:先得到 key所在的table,再像HashMap一样get、中间并不加锁

    get:先得到所属的table,加锁、判断table是否要扩容(如果table要扩容,则产生newTable、hash值相同的slot整体移到newTable、hash值不同的slot,把oldTable中的所有Entry都复制到newTable中)

    remove:要删除Entry3,则先复制Entry1为Entry1*,Entry1*指向Entry4,再复制Entry2为Entry2*,Entry2*指向Entry1*,最终形成一个两叉的链表。原本的Entry1,Entry2,Entry3会被GC自动回收。

    迭代:ConcurrentHashMap的历遍是从后向前历遍的,因为如果有另一个线程B在执行clear操作时,会把table中的所有slot都置为null,这个操作是从前向后执行。

  说明: hashMap与ConcurentHashMap 的相关设计原理与模型, 请参考地址:https://www.cnblogs.com/huanghzm/p/11811592.html

总结

  • 如果涉及到堆栈,队列等操作,应该考虑用List;对于需要快速插入,删除元素,应该使用LinkedList;如果需要快速随机访问元素,应该使用ArrayList。
  • 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高;如果多个线程可能同时操作一个类,应该使用同步的类。
  • 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  • 使用Map时,查找、更新、删除、新增最好使用HashMap或HashTable;对Map进行自然顺序或自定义键顺序遍历时,最好使用TreeMap;
  • 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
  • ArrayList/LinkedList、Hashet/LinkedHashSet是线程不安全的。可以使用synchronized关键字。
  • LinkedList对首部和尾部的插入都支持,但继承自Collection接口的add()方法是在尾部进行插入。

面试的几个问题

ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?

  Vector和HashTable是线程同步的(synchronized),即线程安全的。性能上,ArrayList和HashMap分别比Vector和Hashtable要好。

大致讲解java集合的体系结构

  List、Set、Map是这个集合体系中最主要的三个接口。其中List和Set继承自Collection接口。

  Set不允许元素重复。HashSet和TreeSet是两个主要的实现类。

  List有序且允许元素重复。ArrayList、LinkedList和Vector是三个主要的实现类。

  Map也属于集合系统,但和Collection接口不同。Map是key对value的映射集合,其中key列就是一个集合。key不能重复,但是value可以重复。HashMap、TreeMap和Hashtable是三个主要的实现类。

  SortedSet和SortedMap接口对元素按指定规则排序,SortedMap是对key列进行排序。

Comparable和Comparator区别

  调用java.util.Collections.sort(List list)方法来进行排序的时候,List内的Object都必须实现了Comparable接口。

   java.util.Collections.sort(List list,Comparator c),可以临时声明一个Comparator 来实现排序。

案例:统计一段英文中单词个数,并排序

package main.java;

import java.util.*;

/**
* 统计各个单词的个数,并按照字母顺序排序
*/
public class WordCount { public static void main(String[] args) {
String sentence = "hello, my name is Tom, what is your name? he said: 'my name is John'."; Map<String, Integer> map = countWords(sentence);
sortMap(map); } /**
* 统计单词个数
* @param str
*/
private static Map<String, Integer> countWords(String str) {
int count = 0; Map<String, Integer> map = new HashMap<>(); //用于统计各个单词的个数 StringTokenizer stringTokenizer = new StringTokenizer(str);
while (stringTokenizer.hasMoreElements()) {
count++;
String word = stringTokenizer.nextToken(", ?.!:'\n");
if (map.containsKey(word)) {
int num = map.get(word);
map.put(word, num + 1);
}
else {
map.put(word, 1);
}
}
System.out.println("总共出现单词个数:" + count);
return map;
} /**
* 排序
* @param map
*/
private static List<Map.Entry<String, Integer>> sortMap(Map<String, Integer> map) {
List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 ) {
return (o2.getValue() - o1.getValue());
}
}); //输出排序
for (Map.Entry<String, Integer> obj: list) {
System.out.println(obj.getKey() + " : " + obj.getValue() + "次");
} return list;
}
}

Java基础之Colloction的更多相关文章

  1. Java基础知识(壹)

    写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...

  2. [Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)

    如若转载请注明出处: http://www.cnblogs.com/wang-meng/p/5898837.html   谢谢.上一篇发了一个找工作的面经, 找工作不宜, 希望这一篇的内容能够帮助到大 ...

  3. 【JAVA面试题系列一】面试题总汇--JAVA基础部分

    JAVA基础 基础部分的顺序: 基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法 线程的语法,集合的语法,io 的语法,虚拟机方面的语法 每天几道,持续更新!! 1.一个". ...

  4. 最适合作为Java基础面试题之Singleton模式

    看似只是最简单的一种设计模式,可细细挖掘,static.synchronized.volatile关键字.内部类.对象克隆.序列化.枚举类型.反射和类加载机制等基础却又不易理解透彻的Java知识纷纷呼 ...

  5. java基础练习 字符串,控制流,日历,日期等

    1,对基本控制流程的一些练习 package org.base.practice3; import org.junit.Test; /** * Created with IntelliJ IDEA. ...

  6. Java基础知识【下】( 转载)

    http://blog.csdn.net/silentbalanceyh/article/details/4608360 (最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没 ...

  7. Java基础知识【上】(转载)

    http://blog.csdn.net/silentbalanceyh/article/details/4608272 (最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没 ...

  8. java基础学习03(java基础程序设计)

    java基础程序设计 一.完成的目标 1. 掌握java中的数据类型划分 2. 8种基本数据类型的使用及数据类型转换 3. 位运算.运算符.表达式 4. 判断.循环语句的使用 5. break和con ...

  9. Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

随机推荐

  1. 常用shell备份脚本

    #!/bin/sh # File: /路径/mysql/backup_mydb.sh # Database info DB_NAME="szby" DB_USER="ro ...

  2. BZOJ 2333 [SCOI2011]棘手的操作 (可并堆)

    码农题.. 很显然除了两个全局操作都能用可并堆完成 全局最大值用个multiset记录,每次合并时搞一搞就行了 注意使用multiset删除元素时 如果直接delete一个值,会把和这个值相同的所有元 ...

  3. 获取Linux ip

    第一种方法: 在终端输入命令:ifconfig ip显示为红线标注的部分. 第二种方法: 在终端输入命令:hostname -I 第三种方法: 在终端输入:ip addr show|grep &quo ...

  4. SpringMVC在对应绑定不同实体,但具有相同属性名的解决方案....

    在springmvc中,可以对前台传递过来的参数进行与后台实体绑定(第二种方式相对较好). 比如: 前台页面: <form action="${pageContext.request. ...

  5. python中字符串逆序的实现

    没有直接的逆序函数,有两种常用方式可将字符串逆序,一为切片,一为利用list的reverse,示例如下: #切片x=' y=x[::-1] #reverse函数 y=list(x) y.reverse ...

  6. Python爬虫之正則表達式

    1.经常使用符号 .  :匹配随意字符,换行符 \n 除外 *  :匹配前一个字符0次或无限次 ? :匹配前一个字符0次或1次 .*  :贪心算法.尽可能的匹配多的字符 .*?  :非贪心算法 () ...

  7. 组件的使用(三)AutoCompleteTextView的使用

    AutoCompleteTextView经常使用的属性: android:completionHint 下拉列表以下的说明性文字 android:completionThreshold 弹出下来列表的 ...

  8. [Angular] Upgrade existing Angular app to Progressive Web App

    If you alread have an existing Angular application and want to upgrade to progressive web app. 1. In ...

  9. An internal error occurred during: &quot;Building workspace&quot;. java.lang.StackOverflowError

    1 错误描写叙述 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveW91MjNoYWk0NQ==/font/5a6L5L2T/fontsize/400/fi ...

  10. 2016.03.02,英语,《Vocabulary Builder》Unit 03

    ambi/amphi: 指on both sides或者around的意思,ambi-来自拉丁语,amphi-来自希腊语.ambidextrous:[ˌæmbi'dekstrəs] adj. 两手俱利 ...