集合的理解和好处
- 可以动态保存任意多个对象,使用比较方便!
- 提供了一系列方便的操作对象的方法:add、remove、set、get等。
- 使用集合添加,删除新元素的示意代码-简洁了。
集合框架体系
Collection单列集合
集合![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/8d040ed01cecee7267a7de363038efa0.png#pic_center)
package com.dnstudy.Collection_;
import java.util.ArrayList;
import java.util.List;
//说明:以Arraylist实现类来演示
public class CollectionMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
//add:添加单个元素
list.add("jack");
list.add(10); //list.add(new Integer(10))
list.add(true);
System.out.println("list = " + list);
System.out.println("------------------------------------------");
//remove:删除指定元素
//list.remove(0); //删除第一个元素
list.remove(true);//指定删除某个元素
System.out.println("list = " + list);
System.out.println("------------------------------------------");
//contains:查找元素是否存在
System.out.println(list.contains("jack"));//true
System.out.println("------------------------------------------");
//size:获取元素个数
System.out.println(list.size()); //2
System.out.println("------------------------------------------");
//isEmpty:判断是否为空
System.out.println(list.isEmpty()); // false
System.out.println("------------------------------------------");
//claer:清空
list.clear();
System.out.println("list = " + list);//list = []
System.out.println("------------------------------------------");
//addAll"添加多个元素
ArrayList list2 = new ArrayList();
list2.add("红楼梦");
list2.add("三国演义");
list.addAll(list2);
System.out.println("list = " + list); //list = [红楼梦, 三国演义]
System.out.println("------------------------------------------");
//containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(list2));//true
System.out.println("------------------------------------------");
//removeAll:删除多个元素
list.add("聊斋");
list.removeAll(list2);
System.out.println("list = " + list); //list = [聊斋]
}
}
Collection接口和常用方法:
- Collection接口遍历元素方式1-使用Iterator(迭代器)
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
- Iterator仅用于遍历集合,Iterator本身并不存放对象。
- Collection接口遍历对象方式2-for循环增强
增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator,本质一样。只能用于遍历集合或数组。
基本语法:
for(元素类型 元素名 :集合名或数组名){
访问元素
}
package com.dn.study.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CollectionExercise {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Dog("小奥",3));
list.add(new Dog("小熊",12));
list.add(new Dog("小庄",30));
//先使用for增强
for(Object dog : list){
System.out.println("dog = " + dog);
}
//使用迭代器
System.out.println("----------------------使用迭代器来遍历------------------");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object dog = iterator.next();
System.out.println("dog = " + dog);
}
}
}
List
- List接口是Collection接口的子接口
- List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复。
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API中List接口的常用实现类有:ArrayList、LinkedList和Vector。
package com.dn.study.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListExercise {
public static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i < 8; i++){
list.add("hello");
}
System.out.println("list = " + list);
System.out.println("--------------------------------");
list.add(1,"瓦吉吉哇");
System.out.println("list = " + list);
System.out.println("--------------------------------");
System.out.println("获取第五个元素:" + list.get(4));//获取第五个元素:hello
System.out.println("--------------------------------");
list.remove(5);
System.out.println("list = " + list);//list = [hello, 瓦吉吉哇, hello, hello, hello, hello, hello, hello]
System.out.println("--------------------------------");
//修改
list.set(6,"红楼梦");
System.out.println("list = " + list);//list = [hello, 瓦吉吉哇, hello, hello, hello, hello, 红楼梦, hello]
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj = " + obj);
}
}
}
ArrayList
基本等同于Vector,线程不安全(执行效率高),在多线程情况下,不建议使用ArrayList
- ArrayList中维护了一个Object类型的数组elementsDate.transient Object[] elementData;
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如需要扩容,则直接扩容elementData为1.5倍。
Vector
Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized。
在开发中,需要线程同步安全时,考虑使用Vector。
无参默认10,满后,就按2倍扩容。
如果指定大小,则每次直接按2倍扩。
LinkedList
底层结构
-
LinkedList底层维护了一个双向链表。
-
LinkedList中维护了两个属性first和last分别指向 首结点和尾结点。
-
每个结点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个结点。最终实现双向链表。
-
所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
全面说明
1.LinkedList底层实现了双向链表和双端队列特点
2.可以添加任意元素(元素可以重复),包括null
3.线程不安全,没有实现同步
ArrayList和LinkedList比较
底层结构 | 增删的效率 | 改查的效率 | |
---|---|---|---|
ArrayList | 可变数组 | 较低 数组扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表追加。 | 较低 |
如何选择ArrarList和LinkedList:
1. 如果我们改查的操作多,选择ArrayList
2. 如果我们增删的操作多,选择LinkedList
3. 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
4. 在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另外一个模块是LinkedList,也就是说,要根据业务来进行选择
Set
- 无序(添加和取出的顺序不一致),没有索引
- 不允许重复元素,所以最多包含一个null
HashSet
- 实现了Set接口
- HashSet实际上是HashMap
public HashSet(){
map = new HashMap<>();
}
- 可以存放null值,但是只能有一个null
- HashSet不保证元素是有序的,取决于hash后,再确定索引的结果
- 不能有重复的元素/对象
- HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75 = 12
- 如果table数组使用到了临界值12,就会扩容到 16 * 2 = 32,新的临界值就是32*0.75=24,依次类推
- 在Java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小 >=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制
LinkedHashSet
- LinkedHashSet是HashSet的子类
- LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
- LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet不允许添重复元素。
LinkeedHashSetSource
-
在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet有head 和 tail)
-
每一个节点有before和after属性,这样可以形成双向链表
-
在添加一个元素时,先求hash值,再求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加【原则和hashset一样】)。
tail.next = newElement //示意代码 newElement.pre = tail tail = newElement;
-
这样的话,我们遍历LinkedHashSet 也能确保插入顺序和遍历顺序一致
Map双列集合
Map
Map接口实现类的特点
- Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
- Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不允许重复,原因和HashSet一样
- Map中的value可以重复
- Map的key可以为null,value也可以为null,注意key为null,只能有一个,value为null,可以多个。
- 常用String类作为Map的key
- key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
package com.dn.study.Map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Map接口的使用
* 特点:(1)存储键值对(2)键不能重复(3)无序
*/
public class Demo1 {
public static void main(String[] args) {
//创建Map集合
Map<String, String> map = new HashMap<>();
// 1. 添加元素
map.put("cn", "中国");
map.put("uk", "英国");
map.put("usa", "美国");
map.put("cn", "zhongguo");
System.out.println("元素个数:" + map.size());
System.out.println(map.toString());
System.out.println("--------------------------------");
//2. 删除
map.remove("usa");
System.out.println("删除之后:" + map.size());
//3. 遍历
//3.1使用keySet()
System.out.println("------KeySet()-------------");
//Set<String> keySet = map.keySet();
for(String key : map.keySet()){
System.out.println(key + "-----" + map.get(key));
}
//3.2使用entrySet()方法
Set<Map.Entry<String, String>> entries = map.entrySet();
for(Map.Entry<String, String> entry : entries){
System.out.println(entry.getKey() + "-------------" + entry.getValue());
}
//4. 判断
System.out.println(map.containsKey("cn")); // true
System.out.println(map.containsValue("泰国")); // false
}
}
HashMap
package com.dn.study.Map;
import java.util.HashMap;
import java.util.Map;
/**
* HashMap集合的使用
* 存储结构:哈希表(数组+链表+红黑树)
*/
public class Demo2 {
public static void main(String[] args) {
//创建集合
HashMap<Student, String> students = new HashMap<Student, String>();
//添加元素
Student s1 = new Student("孙悟空", 100);
Student s2 = new Student("猪八戒", 101);
Student s3 = new Student("沙和尚", 102);
students.put(s1, "北京");
students.put(s2, "上海");
students.put(s3, "杭州");
// students.put(s3, "南京");
students.put(new Student("沙和尚", 102),"南京");
System.out.println("元素个数:" + students.size());
System.out.println(students.toString());
System.out.println("-------------------删除---------------");
//2 删除
students.remove(s1);
System.out.println("删除之后: " + students.size());
System.out.println(students.toString());
System.out.println("-------------遍历--------------------");
//3.1使用keySet();
for(Student key : students.keySet()){
System.out.println(key.toString());
}
//3.2使用entrySet();
for(Map.Entry<Student, String> entry : students.entrySet()){
System.out.println(entry.getKey() +" ---------" + entry.getValue());
}
//4.判断
System.out.println(students.containsKey(new Student("猪八戒", 101)));
System.out.println(students.containsValue("杭州"));
}
}
存储结构:哈希表
重复依据:键的hashCode()方法和equals方法
HashMap源码分析
1 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // hashMap初始容量的大小
2 static final int MAXIMUM_CAPACITY = 1 << 30; // hashMap的数组最大容量
3 static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认加载因子
4 static final int TREEIFY_THRESHOLD = 8; // jdk1.8 当链表长度大于8时,调整成红黑树
5 static final int UNTREEIFY_THRESHOLD = 6; // jdk1.8 当链表长度小于6时,调整成链表
6 static final int MIN_TREEIFY_CAPACITY = 64; // jdk1.8 当链表长度大于8时,并且集合元素个数大于等于64时,调整成红黑树
7 transient Node<K, V> [] table; //哈希表中的数组
8 size; // 元素个数
无参构造
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // 加载因子
}
put方法
public V put(K key, V value){
return putVal(hash(key), key, value, false, true);
}
总结
(1) HashMap刚创建时,table是null, 为了节省空间,当添加第一个元素时,table容量调整为16
(2)当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的·个数。
(3)jdk1.8 当链表长度大于8,并且元素个数大于等于64时,会调整成红黑树,目的提高执行效率
(4)jdk1.8 当链表长度小于6时,调整成链表
(5)jdk1.8以前, 链表是头插入,jdk1.8以后是尾插入
Map集合的实现类
-
HashMap【重点】
- Jdk1.2版本,线程不安全,运行效率快;允许用null 作为key或是value。
-
Hashtable :
- JDK1.0版本, 线程安全, 运行效率慢; 不允许null作为key或是value。
-
Properties :
- Hashtable的子类, 要求key和value都是String。通常用于配置文件的读取。
-
TreeMap :
- 实现了SortedMap接口(是Map的子接口), 可以对key自动排序。
package com.dn.study.Map;
import java.util.Map;
import java.util.TreeMap;
/**
* TreeMap的使用
* 存储结构:红黑树
*/
public class Demo3 {
public static void main(String[] args) {
// 新建集合
TreeMap<Student, String> treeMap = new TreeMap<Student, String>();
// 1. 添加元素
Student s1 = new Student("孙悟空", 100);
Student s2 = new Student("猪八戒", 101);
Student s3 = new Student("沙和尚", 102);
treeMap.put(s1, "北京");
treeMap.put(s2, "上海");
treeMap.put(s3, "杭州");
treeMap.put(new Student("沙和尚", 102), "南京");
System.out.println("元素个数:" + treeMap.size());
System.out.println(treeMap.toString());
System.out.println("-----------------------------------");
//2. 删除
// treeMap.remove(new Student("猪八戒", 101));
//System.out.println(treeMap.size());
// 3. 遍历
//3.1使用keySet
for(Student key : treeMap.keySet()){
System.out.println(key + " ------" + treeMap.get(key));
}
System.out.println("---------entrySet()--------------");
for(Map.Entry<Student, String> entry : treeMap.entrySet()){
System.out.println(entry.getKey()+ "-------" + entry.getValue());
}
}
}
Collection工具类
-
概念:集合工具类,定义了除了存取以外的集合常用方法。
-
方法
-
public static void reverse<List<?> list> //反转集合中元素的顺序
-
public static void reverse<List<?> list> //随机重置集合元素的顺序
-
public static void reverse<List<?> list> //升序排序(元素类型必须实现Comparable接口)
-
-
package com.dn.study.Map;
import java.util.*;
public class Demo4 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(20);
list.add(5);
list.add(2);
list.add(30);
list.add(6);
//sort排序
System.out.println("排序之前 :" + list.toString());
Collections.sort(list);
System.out.println("排序之后:" + list.toString());
System.out.println( "---------------------");
// binarySearch二分查找
int i = Collections.binarySearch(list, 5);
System.out.println(i);
//copy复制
List<Integer> dest = new ArrayList<>();
for(int k = 0; k < list.size(); k++){
dest.add(0);
}
Collections.copy(dest, list);
System.out.println(dest.toString());
//reverse反转
Collections.reverse(list);
System.out.println("反转之后:" + list.toString());
//shuffle 打乱
Collections.shuffle(list);
System.out.println("打乱之后:" + list);
// 补充: list转成数组
Integer[] arr = list.toArray(new Integer[10]);
System.out.println(arr.length);
System.out.println(Arrays.toString(arr));
//数组转成集合
System.out.println("---------数组转成集合--------------------");
String[] names = {"张三", "李四", "王五"};
//集合是一个受限集合,不能添加和删除
List<String> list2 = Arrays.asList(names);
// list2.add("赵六");
// list2.remove(0);
System.out.println(list2);
//把基本类型数组转成集合时, 需要修改为包装类型
Integer[] nums = {100, 200, 300, 400, 500};
List<Integer> list3 = Arrays.asList(nums);
System.out.println(list3);
}
}
集合总结:
- 集合的概念:
对象的容器,和数组类似,定义了对多个对象进行操作的常用方法。
-
List集合:
有序、有下标、元素可以重复。(ArrayList、LinkedList、Vector)
-
Set集合:
无序、无下标、元素不可重复。(HashSet、TreeSet)
- Map集合:
存储一对数据,无序,无下标,键不可重复,值可重复。(HashMap, hashTable, TreeMap).
-
Collections:
集合工具类,定义了除了存取以外的集合常用方法。