Java 集合介绍
一、集合介绍
1、介绍
集合又称容器,是Java中对数据结构(数据存储方式)的具体实现。
我们可以利用集合存放数据,也可以对集合进行新增、删除、修改、查看等操作。
集合数据都是在内存中,当程序关闭或重启后集合中数据会丢失,所以集合是一种临时存储数据的容器。
2、JDK中集合结构图
集合作为一个容器,可以存储多个元素,但是由于数据结构的不同,Java提供了多种集合类,将集合类中共性的功能,不断向上抽取,最终形成了集合体系结构。
2.1 List 接口和 Set 接口
1、 List 接口:存储有序(插入顺序),可重复数据
- Vector: List 的实现类,底层为可变长度数据实现。所有方法都是同步操作(线程安全),每次扩容成本增长。新数组长度为原数组长度的2倍。
- ArrayList:List 实现类,底层为可变长度数组实现。所有方法都是非同步操作(非线程安全的),以1.5倍的方式在扩容。常用于:较多查询的情况。
- LinkedList:List的实现类,双向非循环链表的实现。常用于:删、增较多的情况。
总结
ArrayList 和 Vector比较:
相同点:
底层都是可变长数组实现,存储的元素有序可重复
不同点:
ArrayList 扩容后为之前长度的 1.5倍,非同步操作(线程不安全),性能更好
Vector扩容后为之前长度的 2 倍,同步操作(线程安全),性能差
ArrayList 和 LinkedList:
相同点:
存储的元素有序可重复,都是非同步操作(线程不安全)
不同点:
底层实现不同,ArrayList 数组实现,LinkedList 双向链表实现
2、Set 接口:存储无序,不可重复数据
- HashSet:Set 实现类,底层是 HashMap 散列表(数组 + 链表 + 红黑树 jdk1.8及之后)。所有添加到 HashSet 中的元素实际存储到了 HashMap 的 key中。
- LinkedHashSet:HashSet 子类,使用 LinkedHashMap 来存储它的元素,存储的值插入到 LinkedHashMap 的可以 key中,底层实现(数组 + 链表 + (红黑树 jdk1.8及之后)+ 链表),可以记录插入的顺序
- TreeSet:Set 实现类,底层是 TreeMap(红黑树实现),存入到 TreeSet 中的元素,实际存储到了 TreeMap 中,根据存储元素的大小可以进行排序。
总结:
ArrayList 和 HashSet
不同点:ArrayList 存储的数据有序有下标,元素可以重复(底层原因,使用的是数组),可以根据下标获取,HashSet 存储的元素没有下标,没有顺序,元素不可以重复(底层原因,使用 HashMap 存储 数组 + 链表 + (红黑树jdk1.8)之后)
LinkedHashSet 和 TreeSet
相同点:有顺序
不同点:LinkedHashSet 底层 LinkedHashMap 保证插入的顺序
TreeSet 底层 TreeMap(红黑树)保证 key 值大小的顺序。
2.2 Map 接口
Map:独立的接口。每个元素都包含key(名称)和 value(要存储的值)两个值。
- HashMap:Map 实现类,对散列表(数组+链表+(红黑树 jdk1.8及之后))的具体实现,非同步操作(非线程安全的)。存储时以Entry 类型存储(key, value)
- LinkedHashMap:HashMap 的子类,是基于 HashMap 和链表来实现的。在 HashMap 存储结构值上再添加链表,链表只是为了保证顺序
- TreeMap:Map 实现类,使用的不是散列表,而是对红黑树的具体实现。根据 key 值的大小,放入红黑树中,可实现排序的功能(key 值大小排序)
- HashTable:Map 实现类,和 HashMap 数据结构一样,采用散列表(数组 + 链表 + (红黑树 jdk1.8及之后))的方法实现,对外提供的 public 函数几乎都是同步的 (线程安全)。
总结:
HashMap 和 HashTable
相同点:
存储的数据结构一样,插入的数据无序
不同点:
- HashMap 非同步操作(线程不安全),HashTable 同步操作(线程安全)
- HashMap 允许只能有一个 key 为 null 值,因为 hashmap 如果 key 值相同,新的 value 将替代旧的。HashTable 不允许 null 值
LinkedHashMap 和 TreeMap
相同点:有顺序
不同点:
- 底层实现不同
- LinkedHashMap 通过(数组 + 链表 + (红黑树 jdk1.8 及之后))和 链表保证插入的顺序,TreeMap 通过红黑树保证 key 值大小的顺序
常用的集合:ArrayList,HashMap,HashSet
Collection 接口
1、接口
List 和 Set 接口的父接口,还有其他的实现类或子接口。
2、继承关系
3、包含的API
泛型中<? extends E> 代表:只要 E 类型或 E 类型的子类都可以
三、List接口
1、介绍
Collection 接口的子接口。Collection 中包含的内容 List 接口中可以继承。
List 专门存储有序,可重复数据的接口
2、包含的 API
四、ArrayList
1、介绍
实现了List接口,实现底层数组扩容
存储有序、可重复数组,有下标
2、实例化
常用向上转型进行实例化。绝大多数集合都支持泛型,如果不写泛型认为泛型是,使用集合时建议一定要指定泛型。
List<泛型类型> 对象 = new ArrayList<>()
3、内存结构图
4、常用 API
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class TestArrayList {
/*
* ArrayList 的基本使用:
* ArrayList 自身可以动态调整大小
* ArrayList 的内部使用数组存储元素,当数组将被存满,就会创建一个新数组,其容量是当前数组的 1.5 倍
* 同时,所有元素都将移至新数组,假设内部数组已满,而我们现在又添加了一个元素,ArrayList 容量就会以相同
* 的比例扩展(在这种情况下,内部数组中将有一些未分配的空间)。此时,trimToSize()方法可以删除未分配的空间
* 并更改 ArrayList 的容量,使其等于 ArrayList 中的元素个数
* */
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//1.增加元素
list.add("Tom");
list.add("Marry");
list.add("Andy");
list.add("Jhon");
System.out.println(list);
//2.访问元素
/*System.out.println(list.get(1));*/ // 通过元素下标访问第二个元素
//3.修改元素
/* list.set(1, "ZS");
System.out.println(list);*/
//4.删除元素
/*list.remove(1);
System.out.println(list);*/
//5.计算大小
/*int length = list.size();
System.out.println(length);*/
//6、迭代数组列表
/*for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}*/
//7.使用for-each 来迭代元素
/*for (String s : list) {
System.out.println(s);
}*/
//8.ArrayList 排序
//8.1 使用 Arrays 自带方法 sort
//正序
/*list.sort(Comparator.naturalOrder());
System.out.println(list);
//倒序
list.sort(Comparator.reverseOrder());
System.out.println(list);*/
//9.插入元素到指定为止
/*list.add(2, "Pm");
System.out.println(list.toString());*/
//10.添加集合中所有元素到 arrayList 中
/*ArrayList<String> list1 = new ArrayList<String>();
list1.add("GOD");
list1.add("SD");
//把 list1 所有元素添加到 list 中
list.addAll(list1);
// 在指定位置插入 list1
list.addAll(2, list1);
System.out.println(list);*/
//11.删除 arrayList 中所有的元素
//list.clear();
/*list.removeAll(list);
System.out.println("所有 clear() 方法后: " + list);*/
//12.赋值一份 arrayList
//clone 属于浅拷贝,浅拷贝只是赋值指向某个对象的指针,而不复制对象本身,新旧对象还是共享一块内存,
// 所以如果期中一个对象改变了这个地址,就会影响到另一个对象
//浅拷贝对应的就是深拷贝,深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,
//且修改新对象不会影响原对象
/*ArrayList<String> cloneList = (ArrayList<String>)list.clone();
System.out.println("拷贝 ArrayList:" + cloneList);*/
//13.判断元素是否存在于 arrayList 中
/*System.out.println("Marry 是否存在于 arrayList中");
System.out.println(list.contains("Marry"));*/
//14.获取元素的索引值
/*int index = list.indexOf("Marry");
System.out.println("Marry 的索引位置: " + index);*/
//15.判断 arrayList 是否为空
/*System.out.println("arrayList 是否为空: " + list.isEmpty());*/
//16.截取 ArrayList 部分元素
/*System.out.println("SubList: " + list.subList(0,1));*/
//17.toArray() 方法将 ArrayList 对象转化为数组
//创建一个新的 String 类型数组
/*String[] arr = new String[list.size()];
list.toArray(arr);
for (String s : arr) {
System.out.println(s);
}*/
//18.设定指定容量大小的 arrayList
/*ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.ensureCapacity(3);
list2.add(1);
list2.add(2);
System.out.println(list2.size());
System.out.println(list2);*/
//19.返回指定元素在 arrayList 中最后一次出现的位置
/*int index = list.lastIndexOf("Marry");
System.out.println("Marry最后一次出现为止: " + index);*/
//20.保留 arrayList 中 在指定集合中也存在的那些元素
//创建另一个动态数组
/*ArrayList<String> site = new ArrayList<String>();
site.add("Marry");
site.add("Jhon");
//保留元素
list.retainAll(site);
System.out.println("保留的元素:" + list);*/
//21.查看 arrayList 是否包含指定集合中的那些元素
//创建一个动态数组
/*ArrayList<String> site = new ArrayList<String>();
site.add("Marry");
site.add("Jhon");
// 检查数组 list 是否包含 site
boolean b = list.containsAll(site);
System.out.println(b);*/
//22.将 arrayList 中的容量调整为数组中的元素个数
//调整容量
/*list.trimToSize();
System.out.println("ArrayList 大小: " + list.size());*/
//23.删除 arrayList 中指定索引之间存在的元素
//删除所有满足特定条件的 arraylist 元素
list.removeIf(e -> e.contains("Jh"));
System.out.println("删除后的 ArrayList: " + list);
}
}
五、泛型集合类型
1、介绍
在集合中泛型都是任意引用类型。既然是任意引用类型,也可以是集合类型。
2、实例化语法
List<List<Integer>> list = new ArrayList<>();
3、内存结构图
4、代码示例
public class Test {
public static void main(String[] args) {
List<List<Integer>> list = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
List<Integer> list2 = new ArrayList<>();
list2.add(11);
list2.add(22);
list2.add(33);
list.add(list1);
list.add(list2);
for (List<Integer> integerList : list) {
for (Integer i : integerList) {
System.out.println(i);
}
}
}
}
六、LinkedList
1、介绍
LinkedList 是 Java 中对双向非循环链表的实现,实现了List 接口。
具有 ArrayList 所有常用方法,额外还添加了头尾操作方法(实现了 Deque 方法),这些方法在 List 接口是不存在的,所以如果希望使用这些头尾操作方法,实例化时不用向上转型。
2、实例化语法
LinkedList<泛型> 对象 = new LinkedList<>()
3、常用 API
ArrayList 里面常用 API 都可以在 LinkedList 中使用。
public class TestLinkedList {
public static void main(String[] args) {
List<String> linkedList = new LinkedList<>();
linkedList.addFirst("aa");
linkedList.addLast("bb");
//获取头结点 没有头结点 java.util.NoSuchElementException
String first = linkedList.getFirst();
System.out.println(first);
//获取尾结点 没有尾结点 java.util.NoSuchElementException
String last = linkedList.getLast();
System.out.println(last);
//获取头结点 没有头结点 返回 null
String s = linkedList.peekFirst();
System.out.println(s);
//获取尾结点 没有尾结点 放回 null
String s1 = linkedList.peekLast();
System.out.println(s1);
//删除头结点 没有头结点 java.util.NoSuchElementException
String s2 = linkedList.removeFirst();
System.out.println(s2); //被删除结点中的值
//删除尾结点 没有尾结点 java.util.NoSuchElementException
String s3 = linkedList.removeLast();
System.out.println(s3);//被删除结点中的值
//删除头结点 没有头结点 返回 null
String s4 = linkedList.pollFirst();
System.out.println(s4);
//删除尾结点 没有尾结点 放回 null
String s5 = linkedList.pollLast();
System.out.println(s5);
}
}
七、Set 接口
1、介绍
Set 继承了 Collection 接口,继承的都是 Collection 中的方法,没有提供额外的方法。
Set 经常被称为实现无序,不重复数据集合,指的就是 HashSet 实现类。
2、包含API
八、HashSet
1、介绍
完全基于 HashMap (数组 + 链表 + 红黑树)实现的。
存储无序,无下标,元素不重复数据
2、代码示例
public class TestHashSet {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
//添加元素
set.add(1);
set.add(2);
//元素个数
int size = set.size();
//是否包含指定的元素
boolean contains = set.contains(1);
ArrayList<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
list.add(4);
//将其它集合中的元素添加到set集合中
set.addAll(list);
//删除指定的元素
set.remove(2);
//查询元素
for (Integer integer : set) {
System.out.println(integer);
}
}
}
九、TreeSet
1、介绍
底层是基于 TreeMap 红黑树。
public class TestTreeSet {
public static void main(String[] args) {
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(2);
treeSet.add(1);
treeSet.add(3);
//查询元素
for (Integer integer : treeSet) {
System.out.println(integer);
}
}
}
十、Map 接口
1、接口
Map 是独立的接口。和 Collection 没有关系。
Map 中每个元素都是 Entry 类型,每个元素都包含 Key(键)和 Value(值)
2、继承关系
3、包含的API
十一、HashMap
1、介绍
HashMap 是对散列表的具体实现。
里面都包含 Key-Value 值。
2、代码演示
import java.util.HashMap;
public class TestHashMap {
public static void main(String[] args) {
HashMap<Integer, String> sites = new HashMap<>();
sites.put(1, "Google");
sites.put(2, "Runoob");
sites.put(3, "Taobao");
System.out.println(sites);
//1.访问元素
/*System.out.println(sites.get(2));*/
//2.删除元素
/*sites.remove(2);
System.out.println(sites);*/
//3.删除所有键值对
/*sites.clear();
System.out.println(sites);*/
//4.计算大小
/*System.out.println(sites.size());*/
//5.迭代 HashMap
/*for (Integer integer : sites.keySet()) {
System.out.println("key:" + integer + "value" + sites.get(integer));
}*/
/*for (String value : sites.values()) {
System.out.println(value);
}*/
//6.赋值一份 hashMap
/*Object clone = sites.clone();
System.out.println(clone);*/
/*HashMap<Integer, String> clone = (HashMap<Integer, String>)sites.clone();
System.out.println(clone);*/
//7.检查 HashMap 是否为空
/*boolean empty = sites.isEmpty();
System.out.println(empty);*/
//8.将所有键值对插入到 HashMap
/*HashMap<Integer, String> site1 = new HashMap<>();
site1.put(4, "Tom");
site1.put(5, "Wiki");
sites.putAll(site1);
System.out.println(sites);*/
//9.判断 key 是否存在,不存在则将键/值插入到 HashMap 中
/*sites.putIfAbsent(2, "Wiki");
sites.putIfAbsent(4, "Wiki");
System.out.println(sites);*/
//10.检查 hashMap 中是否存在 指定 key 对应的映射关系
/*boolean b = sites.containsKey(2);
System.out.println(b);*/
//11.检查 hashMap 中是否存在 指定 value 对应的映射关系
/*if (sites.containsValue("Taobao")) {
System.out.println("Taobao存在于 sites 中。");
}*/
//12.替换 hashMap 中指定 key 对应的 value
/*String wiki = sites.replace(2, "Wiki");
System.out.println(wiki);
System.out.println(sites);*/
/*boolean replace = sites.replace(2, "Runoob", "Wiki");
System.out.println(replace);
System.out.println(sites);*/
//13.将 hashMap 中所有映射关系替换成给定的函数所执行的结果
/*sites.replaceAll((key, value) -> value.toUpperCase());*/
/*System.out.println(sites);*/
//14.获取指定的 key,不存在返回默认值
/*String wiki = sites.getOrDefault(2, "Wiki");
System.out.println(wiki);*/
//15.forEach()方法的使用
/*sites.forEach((key, value) -> {
value = value + "--HAHA";
System.out.println(key + "=" + value + "");
});*/
//16.返回映射中包含的映射的 set 视图
/*System.out.println(sites.entrySet());*/
//17.获取映射 中所有 key 组成的 set 视图
/*System.out.println(sites.keySet());*/
//18.获取映射 中所有 value 组成的 set 视图
/*System.out.println(sites.values());*/
//19.merge 先判断指定的 key 是否存在,如果不存在,则添加键值对到 hashMap 中
/*String wiki = sites.merge(4, "Wiki", (oldVaule, newValue) -> oldVaule + newValue);
System.out.println(wiki);
System.out.println(sites);*/
//20.compute() 方法对 hashMap 中指定的 key 的值进行重新计算
/*String compute = sites.compute(3, (key, value) -> value + "HAHA");
System.out.println(compute);
System.out.println(sites);*/
//21.对 hashMap 中指定的 key 值进行重新计算,如果不存在这个 key,则添加到 hashMap 中
/*String s = sites.computeIfAbsent(4, key -> "Wiki");
System.out.println(s);
System.out.println(sites);*/
//22.对 hashMap 中指定的 key 的值进行重新计算,前提 是该 key 存在于 hashMap 中
String s = sites.computeIfPresent(3, (key, value) -> value + "HAHAHA");
System.out.println(s);
System.out.println(sites);
}
}
十二、TreeMap
1、简介
红黑树的具体实现
2、代码实例
总体和 HashMap 使用非常类似
public class TestTreeMap {
public static void main(String[] args) {
Map<Integer, String> treeMap = new TreeMap<>();
treeMap.put(2, "bb");
treeMap.put(1, "aa");
treeMap.put(3, "cc");
for (Map.Entry<Integer, String> entry : treeMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
十三、Iterator
1、简介
迭代器,是一个接口,每个集合中实现类都对 Iterator 提供了内部的实现。
通过 Iterator 可以实现遍历集合的效果。
存在意义:
隐藏集合实现细节,无论是哪种集合都是通过 Iterator 进行操作,而不是直接操作集合。通过一套 API 实现所有集合的变量。
可以在遍历时删除集合中的值。
2、实例化
每个实现类都提供了 .iterator();返回值就是迭代器对象。
public class TestIterator {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//获取集合的迭代器
Iterator<Integer> iterator = list.iterator();
//.hasNext() 判断是否有下一个元素
while (iterator.hasNext()) {
//获取下一个元素
Integer next = iterator.next();
System.out.println(next);
}
}
}
3. ConcurrentModificationException
在循环遍历集合时,向集合中插入值或删除集合中值时会出现这个异常。
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
for (Integer integer : list) {
list.remove(integer);
}
}
}
异常截图:
为什么使用 for(int i = 0; i < list.size(); i++){} 时不出现这个异常?
因为这种循环其实是多次执行 get,调用 get() 方法时,集合 对其他元素删除 或新增是没有要求的。而增强 for 循环是把集合看作哟个整体,在遍历集合时,不允许对整个集合进行操作。
4、遍历集合时删除元素的内容
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//获取集合的迭代器
Iterator<Integer> iterator = list.iterator();
//.hasNext() 判断是否有下一个元素
while (iterator.hasNext()) {
//获取下一个元素
Integer next = iterator.next();
//删除当前元素
iterator.remove();
}
}
}
十四、Collections
1、介绍
Collections 是一个工具类型,一个抓们操作集合的工具类。
2、代码示例
public class TestCollections {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
//将集合中的元素排序
Collections.sort(list);
for (Integer integer : list) {
System.out.println(integer);
}
//将多个元素添加到集合中
Collections.addAll(list, 4, 6, 5);
//获取集合中最大值
Integer max = Collections.max(list);
//获取集合中最小值
Integer min = Collections.min(list);
}
}
十五、不定项参数
1、简介
不定项参数必须在方法定义时,最后一个方法参数。
调用包含不定项参数的方法时,不定项参数位置,可以传递 0 到 任意个指定类型的参数。
方法内部,把不定项参数当做 数组使用即可。
2、代码示例
public class TestArgs {
public static void main(String[] args) {
test("zs"); //没有传值, 数组长度为0
test("ls", 1, 2, 3); //传值, 数组长度为3
}
public static void test(String name, int... i){
System.out.println(i);
System.out.println(Arrays.toString(i));
}
}