什么是集合?
Java集合类是一种特别有用的工具类,可用于存储数量不等的对象
Java集合包含List、Set、Map、Queue四种体系
Java集合类主要由两个接口派生而出:Collection和Map
四种存储结构
List代表有序、可重复集合
Queue代表队列特性
Set代表无序、不可重复集合
Map代表存储映射关系的集合
Collection接口及实现
Map接口及实现
List集合体系及应用
List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引
List集合允许使用重复元素,通过索引访问指定位置的元素
List集合默认按元素的添加顺序设置元素的索引
List接口与ArrayList实现类
ArrayList的特点
ArrayList基于数组实现的List类,是Java数组的有效替代品
ArrayList会自动对容量进行扩容,多数情况下无须指定最大长度
ArrayList的数据在内存中是连续紧密存储的,基于数据访问速度快
类似javascript中的Array
package cn.chengaofeng;
import java.util.ArrayList;
import org.junit.Test;
public class ArrayListTest {
@Test
public void testCreateArrayList() {
ArrayList<Number> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
assert arrayList.size() == 3;
assert arrayList.get(0).equals(1);
assert arrayList.get(1).equals(2);
assert arrayList.get(2).equals(3);
}
@Test
public void testUpdateArrayList() {
ArrayList<Number> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
// 通过索引添加元素
arrayList.add(1, 4);
assert arrayList.size() == 4;
assert arrayList.get(0).equals(1);
assert arrayList.get(1).equals(4);
assert arrayList.get(2).equals(2);
assert arrayList.get(3).equals(3);
// 通过set更新元素
arrayList.set(2, 5);
assert arrayList.get(2).equals(5);
// 通过remove删除元素
arrayList.remove(1);
assert arrayList.size() == 3;
}
}
LinkedList特征与方法应用
LinkedList同时实现了List与Deque两个接口
LinkedList在保障有序、允许重复的前提下,也可以作为队列在队首、队尾快速追加数据
LinkedList的数据在内存中是分散存储的,基于链表,拥有良好的数据插入速度,但数据访问速度低于ArrayList
package cn.chengaofeng;
import java.util.LinkedList;
import org.junit.Test;
public class LinkedListTest {
@Test
public void testLinkedList() {
LinkedList<String> linkedList = new LinkedList<>();
// 跟ArrayList一样,LinkedList也实现了List接口,所以可以使用List接口中的方法
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
assert linkedList.size() == 3;
assert linkedList.get(0).equals("a");
assert linkedList.get(1).equals("b");
assert linkedList.get(2).equals("c");
// LinkedList还实现了Deque(双端队列)接口,所以可以使用Deque接口中的方法
linkedList.addFirst("d");
linkedList.addLast("e");
assert linkedList.size() == 5;
assert linkedList.getFirst().equals("d");
assert linkedList.getLast().equals("e");
}
}
ArrayList与LinkedList存储结构
三种集合的遍历方式
for循环遍历
forEach方法遍历
Iterator迭代器遍历
推荐阅读:如何理解Java的迭代器?
package cn.chengaofeng;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
public class ListLoopTest {
@Test
public void testListLoop() {
List<String> listLoop = new ArrayList<>();
listLoop.add("a");
listLoop.add("b");
listLoop.add("c");
for (String s : listLoop) {
System.out.println(s);
}
listLoop.forEach(s -> System.out.println(s));
Iterator<String> iterator = listLoop.iterator();
// 使用迭代器遍历集合
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
}
}
Set集合体系及应用
Set集合代表一个元素无序、不可重复的集合
Set集合与List集合使用方法基本相同,只是处理行为略有不同
Set集合常用的实现类是:HashSet与TreeSet
package cn.chengaofeng;
import java.util.HashSet;
import org.junit.Test;
public class HashSetTest {
@Test
public void testCreateHashSet() {
// HashSet是Set接口的一个实现类,所以可以使用Set接口中的方法
HashSet<String> hashSet = new HashSet<>();
hashSet.add("a");
hashSet.add("b");
hashSet.add("c");
assert hashSet.size() == 3;
assert hashSet.contains("a");
assert hashSet.contains("b");
assert hashSet.contains("c");
hashSet.add("a");
assert hashSet.size() == 3;
// 无序的,无法使用get方法获取指定索引的元素
}
}
Set集合如何确保数据的唯一性?
Set集合在新增数据时先判断数据的hashCode()是否已存在,若hashCode()在Set集合存在再调用equals()进行值比较;hashCode()与equals()都存在的情况下,Set集合才认为数据已存在,不予新增。
为什么要用对象的hashCode()直接用equals()判断不行吗?
出于执行效率考虑,hashCode()返回的整数结果决定了Set集合中的存放位置,hashCode()计算速度很快,但可能出现哈希碰撞;equals()则对值进行比较,处理速度相对较慢。
HashSet存储原理
HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类
HashSet按Hash算法来决定集合元素的顺序,具有很好的查找性能
当向HashSet集合中存入一个元素时,根据该对象的hashCode值决定该对象在HashSet中的存储位置
Hash是什么?
Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的数据通过散列算法变换成固定的输出,该输出就是散列值。
LinkedHashSet
LinkedHashSet是HashSet的子类,除HashSet的特性外,它同时使用链表维护元素的次序,可以保障按插入顺序提取数据
LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能
迭代访问Set里的全部元素时将有很的性能,因为它以链表来维护内部顺序
package cn.chengaofeng;
import java.util.LinkedHashSet;
import org.junit.Test;
public class LinkedHashSetTest {
@Test
public void testCreateHashSet() {
LinkedHashSet<String> hashSet = new LinkedHashSet<>();
hashSet.add("a");
hashSet.add("b");
hashSet.add("c");
assert hashSet.size() == 3;
System.out.println(hashSet);
// 输出结果:[a, b, c] 有序的,按照添加的顺序输出
}
}
TreeSet存储原理
TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态
TreeSet采用红黑树的数据结构来存储集合元素
TreeSet默认采用自然排序对元素升序排列,也可以实现Comparable接口自定义排序方式
package cn.chengaofeng;
import java.util.TreeSet;
import org.junit.Test;
public class TreeSetTest {
class IntegerComparator implements java.util.Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
@Test
public void testCreateHashSet() {
TreeSet<Integer> set = new TreeSet<>(new IntegerComparator());
set.add(1);
set.add(2);
set.add(3);
assert set.size() == 3;
assert set.contains(1);
System.out.println(set);
}
}
Map映射体系及其应用
Map映射特点
Map用于保存具有映射关系的数据,每组映射都是Key与Value组合而成
Key与Value可以是任何引用类型数据,但是Key通常是String
Map中的Key不允许重复,重复为同一个Key设置Value,后者Value会覆盖前者Value
HashMap和LinkedHashMap
Java先有Map后有Set,HashSet从HashMap精简而来
package cn.chengaofeng;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Test;
public class HashMapTest {
@Test
public void createHashMap() {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("a", "1");
hashMap.put("b", "2");
hashMap.put("c", "3");
assert hashMap.size() == 3;
assert hashMap.get("a").equals("1");
assert hashMap.get("b").equals("2");
assert hashMap.get("c").equals("3");
System.err.println(hashMap);
assert hashMap.containsKey("a") == true;
// 不确定值的类型
HashMap<String, Object> student = new HashMap<>();
student.put("name", "张三");
student.put("age", 18);
int age = (int) student.get("age");
assert age == 18;
}
@Test
public void createLinkedHashMap() {
// 可以保证按照添加的顺序输出
LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("name", "张三");
linkedHashMap.put("age", 18);
System.out.println(linkedHashMap);
}
// 开发中我们会直接使用Map接口,而不是HashMap或LinkedHashMap
@Test
public void createMap() {
// 可以保证按照添加的顺序输出
Map<String, Object> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("name", "张三");
linkedHashMap.put("age", 18);
System.out.println(linkedHashMap);
}
}
TreeMap
TreeMap存储key-value对时,需要根据key对节点进行排序
TreeMap支持两种Key排序:自然排序和定制排序
与TreeSet相同,TreeMap也是基于红黑树结构对数据进行排序
package cn.chengaofeng;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import org.junit.Test;
public class TreeMapTest {
class RecordComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
@Test
public void createTreeMap() {
Map<String, Object> record = new TreeMap<>(new RecordComparator());
record.put("name", "张三");
record.put("c", "cccc");
record.put("age", 18);
record.put("b", "bbbb");
System.out.println(record);
}
}
Map的遍历方式
package cn.chengaofeng;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.junit.Test;
public class TreeMapTest {
@Test
public void forEachMap() {
Map<String, Object> record = new TreeMap<>(new RecordComparator());
record.put("name", "张三");
record.put("c", "cccc");
record.put("age", 18);
record.put("b", "bbbb");
Set<String> set = record.keySet();
for (String k : set) {
System.out.println(k + " : " + record.get(k));
}
// 推荐使用
record.forEach((k, v) -> {
System.out.println(k + " : " + v);
});
Iterator<Map.Entry<String, Object>> it = record.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
应用Collections实现集合排序
Collections实现集合排序
package cn.chengaofeng;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.junit.Test;
public class ListSorterTest {
@Test
public void sortList() {
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
list.add(4);
list.add(5);
list.add(6);
assert list.get(0) == 3;
assert list.get(1) == 1;
System.out.println(list);
Collections.sort(list); // 会修改原list
System.out.println(list);
}
class RecordComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}
@Test
public void sortList2() {
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
list.add(4);
list.add(5);
list.add(6);
assert list.get(0) == 3;
assert list.get(1) == 1;
System.out.println(list);
Collections.sort(list, new RecordComparator()); // 会修改原list
System.out.println(list);
}
}
自定义类型,如何通过集合排序?
package cn.chengaofeng;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
public class CustomSortTest {
class User {
public int id;
public String name;
public int age;
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
class UserComparator implements java.util.Comparator<User> {
@Override
public int compare(User o1, User o2) {
// 字符串用 o1.name.compareTo(o2.name)
return o1.id - o2.id;
}
}
@Test
public void sortList() {
List<User> list = new ArrayList<>();
list.add(new User(1, "张三", 18));
list.add(new User(5, "孙七", 22));
list.add(new User(2, "李四", 20));
list.add(new User(4, "赵六", 21));
list.add(new User(6, "周八", 23));
list.add(new User(3, "王五", 19));
assert list.get(0).id == 1;
assert list.get(1).id == 5;
list.forEach(u -> System.out.println(u.id));
Collections.sort(list, new UserComparator()); // 会修改原list
list.forEach(u -> System.out.println(u.id));
}
}
总结
Java集合概述
集合类型:List、Set、Map、Queue。
核心接口:Collection和Map。
List接口
特点:有序、可重复、允许使用索引。
实现类:
ArrayList:基于动态数组实现,支持快速随机访问。
LinkedList:基于双向链表实现,支持快速插入和删除。
Set接口
特点:无序、不可重复。
实现类:
HashSet:基于哈希表实现,不保证元素顺序。
LinkedHashSet:基于哈希表和链表实现,维护插入顺序。
TreeSet:基于红黑树实现,元素处于排序状态。
Map接口
特点:存储键值对,键唯一。
实现类:
HashMap:基于哈希表实现,不保证映射顺序。
LinkedHashMap:基于哈希表和链表实现,维护插入顺序。
TreeMap:基于红黑树实现,键处于排序状态。
集合遍历方式
for循环:直接遍历集合。
forEach:使用Lambda表达式遍历。
Iterator:使用迭代器遍历。
集合排序
Collections.sort():对List集合进行排序。
Comparator:自定义比较器,用于自定义排序规则。
集合操作
添加:
add()
删除:
remove()
访问:
get()
(List)、contains()
、containsKey()
更新:
set()
(List)、put()
(Map)
集合特性
线程安全:某些集合如
Vector
和Hashtable
是线程安全的,但性能较低。Collections.synchronizedList()
和Collections.synchronizedMap()
可以提供线程安全的包装。并发集合:
ConcurrentHashMap
、ConcurrentLinkedQueue
等提供了线程安全的并发访问。
记忆模型
List:想象成书架,每本书(元素)都有固定位置(索引)。
Set:想象成没有重复的艺术品收藏,每件艺术品(元素)都是独一无二的。
Map:想象成图书馆的目录系统,每个目录条目(键)都指向特定的书籍(值)。