”在java开发中,集合框架类非常常用,它比数组更加面向对象、灵活和实用,能够应用于开发中的不同场景,提高我们的开发效率。”
首先来看一下Java类中的集合家族,从百科上拉下来的一张集合关系图。其中有一些关系是错的,我已根据Java源码中的类关系修正了一下。
可能不是很全面,有些类没有画出,比如LinkedHashMap,继承HashMap实现Map接口,以及LinkedHashSet继承HashSet实现Set接口,还有IdentityHashMap继承AbstractMap实现Map接口等。
为了能够更加直观的理解和对比这些集合类,我做了一个表格,简单的列出了它们的关系、特性和使用场景。
具体集合类 | 简要说明 |
---|---|
HashMap | 常用类,满足通用要求,键值对操作,键值均可为任意Object子类对象。 |
SortedMap | 可排序Map接口,一般使用实现类-TreeMap。 |
TreeMap | 支持对key排序,要求key实现Comparable接口或使用Comparator构造它。 |
LinkedHashMap | 保留键插入顺序,用equals比较键值 |
IdentityHashMap | 使用==来检查键值相等性。 |
ArrayList | 基于索引的快速元素访问。 |
LinkedList | 对任意位置的元素访问和删除支持较好,成员访问速度较差。 |
Vector | 线性安全,单个元素 |
Stack | 栈类,后进先出(FILO) |
SortedSet | 排序接口,其实现类-TreeSet |
HashSet | 满足通用键值操作需求,实现equals方法记得需同时修改hashCode方法 |
TreeSet | 内部自动排序,外部可以有序遍历元素。成员须实现Comparable接口,或用Comparator定制排序。 |
LinkedHashSet | 遍历时按成员插入顺序获取 |
常用的集合有:Map、List、Set这三种,它们提供接口,再由抽象接口继承,然后再是诸如ArrayList、HashMap的具体实现。我们一般用到的也是这些ArrayList等的实现类来进行集合操作。下面就分别对这三种集合类进行特性总结和一些集合API的操作。
键值对集合Map
Java中的集合框架类,第一类是Collection接口,第二类是单独Map集合接口。所以注意Map不是继承Collection接口
的。集合类都在java.util包下面。
Map是键值对(Key-Value)操作,存储的键值对信息。Key和Value的类型可以是任意的Object或Object子类,Key是唯一的,Value可以重复,可为null。可以根据键(Key)得到值(Value)。
Map集合在应用中总的使用场景就是当我们需要通过唯一的键去保存和获取值的时候,或者方法接收参数或返回数据时需要多个值且这些值可能需要唯一的键再去匹配查找的时候。而不同的Map接口实现类有不同的使用场景。
Map接口的常用的实现类有:
Map
--- HashMap:常用Map,哈希算法实现,根据键的hashCode值存储数据,通过键直接获取值,有着很快的访问速度。
--- TreeMap:可对Key进行排序,Key须实现Comparable接口或构造器自定制Comparator实现。
--- LinkedHashMp:维护着一个双向链表,存储的数据是有序的。
--- HashTable:线性安全,键和值都不允许为null。
--- Properties:键值都是String类型,常用作读取配置文件。
Map的常用方法:
方法名 | 说明 |
---|---|
Object put(Object key,Object value) | 插入一个键值对 |
void putAll(Map extends K, ? extends V> m) | 把另一个Map集合元素全部插入到当前Map集合中 |
Object remove(Object key) | 根据Key移除值,并将该值返回 |
void clear() | 根据Key移除值,并将该值返回 |
Object get(Object key) | 根据Key获取值 |
boolean containsKey(Object key) | 判断是否包含某个Key |
boolean containsValue(Object value) | 判断是否包含某个Value |
1.HashMap
HashMao是最常用的一种集合。其底层实现是使用哈希算法,来建立键值和真实值之间的对应关系,使得其具有快速存取数据的能力。
HashMap的基本用法示例
/**
* 测试HashMap
*/
public static void hashMapTest() {
//=======1.HashMap========
Map hashMap = new HashMap();
hashMap.put("one",32.0);
hashMap.put("two",64.0);
hashMap.put("three",128.0);
//插入null的key
hashMap.put(null,9.0);
hashMap.put("three",8.0);
Map sonHashMap = new HashMap();
sonHashMap.put("tt",322.0);
sonHashMap.put("bb",624.0);
//putAll插入子集Map的所有元素
hashMap.putAll(sonHashMap);
//remove一个元素
Double rmObj = hashMap.remove("one");
//获取所有的Key
Set keySet = hashMap.keySet();
//获取一个实现Map.Entry的Set
Set> entrySet = hashMap.entrySet();
//根据key获取Value
Double twoElement = hashMap.get("two");
//判断是否存在某个key或value
boolean isContaimsOneKey = hashMap.containsKey("one");
boolean isContaimsOneValue = hashMap.containsValue(32.0);
//遍历集合Map的方法
//1.增强for循环遍历
for (Map.Entry entry : hashMap.entrySet()) {
//获取key
String key = entry.getKey();
//获取value
Double value = entry.getValue();
}
//遍历key方式
for (String keyStr : hashMap.keySet()) {
//获取value
Double vaString = hashMap.get(keyStr);
}
//清空
hashMap.clear();
//2.使用Iterator
Iterator iterator = hashMap.keySet().iterator();
while (iterator.hasNext()) {
//获取key
String key = (String) iterator.next();
//获取value
Double vaDouble = hashMap.get(key);
//如果不存在这个key就返回默认值
Double defDouble = hashMap.getOrDefault(key, 0.0);
}
}
2.TreeMap
TreeMap添加元素后,会根据实现了Comparable的Key进行排序,遍历它后取出的元素也是有序的。也可以自定义排序方法。
TreeMap基本使用示例
/**
* 测试TreeMap
*/
public static void TreeMapTest() {
/**
* =======2.TreeMap========
* 可自动根据Key排序,因为像Double这类的对象都实现了Comparable接口的了
*/
Map treeMap = new TreeMap();
treeMap.put(3.0, "ttt");
treeMap.put(5.0, "fff");
treeMap.put(2.0, "ggg");
treeMap.put(1.0, "qqq");
treeMap.put(6.0, "sss");
for (Double key : treeMap.keySet()) {
System.out.println(key+"<-->"+treeMap.get(key));
/**
* 输出:
* 1.0<-->qqq
* 2.0<-->ggg
* 3.0<-->ttt
* 5.0<-->fff
* 6.0<-->sss
*/
}
/**
* 定制排序
*/
Map treeMap2 =
new TreeMap(new Comparator() {
@Override
public int compare(Double o1, Double o2) {
if (o2 != null) {
//降序
return o2.compareTo(o1);
}
return 0;
}
});
treeMap2.put(3.0, "ttt");
treeMap2.put(5.0, "fff");
treeMap2.put(2.0, "ggg");
treeMap2.put(1.0, "qqq");
treeMap2.put(6.0, "sss");
System.out.println("定制排序:");
for (Double key : treeMap2.keySet()) {
System.out.println(key+"<-->"+treeMap2.get(key));
}
}
3.LinkedHashMap
LinkedHashMap存储的数据是有序的,它底层实现的是一个双向链表。通过指针移动来操作集合中的元素。使用它插入和删除元素效率高,但是访问元素速度慢。当需要对插入元素保留顺序以及插入和删除效率有要求使用它。
LinkedhashMap基本的用法示例
/**
* 测试LinkedHashMp
*/
public static void testLindedHashMap() {
//=======3.LindedHashMap========
Map linkedHashMap = new LinkedHashMap();
//插入
linkedHashMap.put("test11", "vvvv11");
linkedHashMap.put("best22", "vvvv22");
linkedHashMap.put("cest33", "vvvv33");
//保留原来的插入顺序
for (Map.Entry str : linkedHashMap.entrySet()) {
System.out.print(str.getKey() + " ");
//test11 best22 cest33
}
//删除
String reString = linkedHashMap.remove("best22");
System.out.println(reString);
}
其他的操作方法也是大同小异了。
4.HashTable
HashTable线程安全的,同步的,不允许null的键或值。
由于历史原因吧,它是extends Dictionary<>的,即基于陈旧的Dictionary类的。另一个它是线程安全的,同步的,确保集合类的对象的原子性操作,但也会影响到程序的执行效率。而平常不要求线程安全的,一般会使用异步的HashMap,非线程安全,减少同步性能开销,提高执行效率。
/**
* 测试HashTable
* 线程安全,同步,效率低
*/
public static void testHashTable() {
//=======5.HashTable======== extends Dictionary<>
Map hashTable = new Hashtable();
hashTable.put("test", "codexiaoka");
hashTable.put("xiaoka", "编程小咖");
//不能为null,否则报NPE
//hashTable.put("null", null);
//=======5.Properties========,读取配置文件
//Properties props = new Properties();
Properties sysProperties = System.getProperties();
String path = System.getProperty("path");
}
Properties是继承HashTable的,表示一个持久的属性集,通常用来读取配置。
列表集合类List
List接口的常用到的实现类就是ArrayList和LinkedList了。它比数组更加面向对象。ArrayList是由数组构成。LinkedList是由链表构成。选择的时候如果只是为了快速访问,而不会进行频繁的对集合插入和删除操作,对集合元素的顺序也没有太高的要求,那么通常会使用ArrayList。
ArrayList是基于索引(Index)的数据结构,所以访问搜索元素的速度是很快的,时间复杂度是O(1);但插入和删除数据开销大,因为会涉及更新索引,会导致重排数组中的所有数据。
LinkedList插入和删除元素效率是优于ArrayList的,因为它不需要改变链表大小和重排数据,插入和删除的时间复杂度是O(1)。
除了需要比较频繁的插入和删除数据而搜索数据比较少时,可以使用LinkedList,否则大多情况下都使用ArrayList。
基本用法示例:
/**
* 测试ArrayList
*/
public static void arrayList() {
//数组
//String[] strings = new String[] {"ddd","ccc"};
//一串String转为List
List list = Arrays.asList("aaa","bbbb");
ArrayList arrayList = new ArrayList();
//添加元素
arrayList.add("tesetqq");
arrayList.add("haha");
arrayList.addAll(list);
//移除
arrayList.remove("haha");
arrayList.remove(0);
//清空
arrayList.clear();
arrayList.size();
//遍历
for (int i = 0;arrayList != null && i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
for (String seString : arrayList) {
System.out.println(seString);
}
Iterator it = arrayList.iterator();
while (it.hasNext()) {
String string = it.next();
}
}
/**
* 测试LinkedList
*/
public static void linkedList() {
LinkedList linkedList = new LinkedList();
linkedList.add("ddd");
linkedList.remove(0);
//其他类似
linkedList.clear();
//遍历
for (int i = 0;linkedList != null && i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
//遍历. 增强for效率更高
for (String string : linkedList) {
System.out.println(string);
}
}
这里注意的是两种集合的遍历,一般ArrayList使用普通for循环遍历更快,而LinkedList使用增强for循环(foreach,底层实现是迭代器)效率更高。
不重复存储集合Set
Set的实现类常用的是HashSet和TreeSet。无序,不允许有重复的数据,可以使用它对数据进行去重,以及支持系统排序和定制排序。
HashSet的底层实现是哈希表;TreeSet底层实现是二叉树、红黑树算法。
基本用法示例:
/**
* HashSet
*/
public static void testHashSet() {
Set set = new HashSet();
set.add("ggg");
set.add("ggg");
set.add("codexiaoka");
//自动去重
for (String string : set) {
System.out.println(string);
}
set.clear();
set.isEmpty();
//迭代器遍历
Iterator it = set.iterator();
}
/**
* TreeSet
*/
public static void testTreeSet() {
//TreeSet treeSet = new TreeSet();
//定制排序,ddd ccc aaa
TreeSet treeSet = new TreeSet(new MyComparator());
treeSet.add("ccc");
treeSet.add("aaa");
treeSet.add("ddd");
//其他操作
// treeSet.clear();
// treeSet.contains("aaa");
// treeSet.first();
//更多参考API
for (String string : treeSet) {
System.out.print(string + " ");
//自动排序:aaa ccc ddd
}
}
public static void main(String[] args) {
testHashSet();
testTreeSet();
}
}
class MyComparator implements Comparator {
@Override
public int compare(String o1, String o2) {
if (o2 != null) {
return o2.compareTo(o1);
}
return 0;
}
最后总结的话
毕竟Java越来越成熟,运用越来越广泛,各种Java的技术知识、培训视频及技术书籍等非常多,就看我们怎么去学习了。在我们使用Java开发遇到问题时,在网上一搜也会有很多解决方法。说明我们普遍遇到的坑可能别人都应该踩过了。
对于集合来说,我们日常开发中一般是运用集合来临时存储数据、取数据、处理数据等,所以要知道集合的各种特性,知道怎么遍历、排序等,就能满足基本的开发需要了。
这里也只是对基本的集合操作类进行了学习和复习,熟悉其特性和基本用法,知道怎么用,什么情景下用,应该就可以了。如果有兴趣学习更多,可以研究下jdk的这些集合源码,这样更加知其然也知其所以然了。