Java集合类
一、Collection集合
概念:对象的容器,实现了对 对象的操作,类似数组功能
集合与数组的区别:数组长度固定,集合长度不固定
数组可以存基本数据类型和引用类型,而集合只能存引用类型
Collection体系
![](https://i-blog.csdnimg.cn/blog_migrate/e077349c0774c9cd348f17566811252b.png)
1.1 Collection类的基本方法
案例:Collection的基本方法以及操作
public class test01Collection {
public static void main(String[] args) {
//1.创建一个集合,用的ArrayList 是有序的
Collection collection = new ArrayList();
//2.添加元素
collection.add("苹果");
collection.add("西瓜");
collection.add("猕猴桃");
System.out.println("元素个数:"+collection.size());
System.out.println(collection);
//3.删除其中一个
collection.remove("苹果");
//4.删除所有
// collection.clear();
//5.遍历集合for each
for (Object item:collection) {
System.out.println(item);
}
//6.使用迭代器遍历
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
String s = (String) iterator.next();
System.out.println(s);
}
//7.判断
System.out.println(collection.contains("苹果"));
System.out.println(collection.isEmpty());
}
}
1.2 List类的基本方法
案例:list的基本方法以及操作
public class test02_List {
public static void main(String[] args) {
//1.list的创建
List list = new ArrayList();
list.add("MMing");
list.add("Aifleft");
list.add("LongSkr");
list.add("Forever");
list.add(4,"lionkk");
System.out.println(list.toString());
//2.删除
list.remove(4);
System.out.println(list.toString());
//3.遍历 fori foreach Iterator ListIterator
//for i
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//for each
for (Object item: list) {
System.out.println(item);
}
// Iterator
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// ListIterator 相对于普通的 Iterator 可以进行前驱和后继的寻找
ListIterator listIterator = list.listIterator();
while (listIterator.hasNext()){
System.out.println(listIterator.next());
}
while (listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
//4.sublist 进行区间截取
List sublist = list.subList(0,2);
System.out.println(sublist.toString());
}
}
1.3 List实现类
1)ArrayList:实现是 数组,查询快,增删慢,运行效率快,但线程不安全
2)LinkedList:实现是 链表,查询慢,增删快
3)Vector: 实现是 数组,查询慢,增删快,运行效率慢,但线程安全
1.3.1 ArrayList
案例:ArrayList的基本方法操作
public class test02_ArrayList {
public static void main(String[] args) {
//1.添加数据到list
ArrayList arrayList = new ArrayList();
Student s1 = new Student("裴珠泫",20);
Student s2 = new Student("金珍妮",20);
Student s3 = new Student("柳智敏",20);
Student s4 = new Student("宁艺卓",20);
Student s5 = new Student("李秀满",50);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
arrayList.add(s4);
arrayList.add(s5);
System.out.println(arrayList.toString());
//2.删除元素
// arrayList.remove(s5);
// System.out.println(arrayList.toString());
//3.删除元素(通过new 方法) 需要重写对应类的equals方法 涉及remove的源码
arrayList.remove(new Student("李秀满",50));
System.out.println(arrayList.toString());
//4.遍历 fori foreach Iterator ListIterator
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
for (Object o:arrayList) {
System.out.println(o);
}
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
ListIterator listIterator = arrayList.listIterator();
while (listIterator.hasNext()){
System.out.println(listIterator.next());
}
while ((listIterator.hasPrevious())){
System.out.println(listIterator.previous());
}
//5.判断
boolean result = arrayList.contains(new Student("裴珠泫",20));
System.out.println(result);
//6.查找
System.out.println(arrayList.indexOf(new Student("金珍妮",20)));
}
}
案例:ArrayList源码分析
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
默认容量:10
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ArrayList的add方法:里面调用了 ensureCapacityInternal方法,传递了一个size+1(新的ArrayList就是1)的参数
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
ensureCapacityInternal方法:保证内部的容量方法(一个缓存方法),接收了size+1(新的ArrayList就是1)参数,继续调用ensureExplicitCapacity方法,参数为calculateCapacity方法
返回的int值
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
calculateCapacity方法:用于得出真实容量的方法,需要两个参数,当前元素数据和minCapacity(size+1)的容量
方法内部进行判断,是否为空数组,如果是,返回默认容量(10)和minCapacity (size+1如果是新的ArrayList就是1)之间大的数字
如果不是空数组就返回
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ensureExplicitCapacity方法:保证明确容量方法,参数就是calculateCapacity的返回值,里面进行了一个判断,
如果minCapacity(与DEFAULT_CAPACITY比较完会变成10)- 当前对象的长度 大于 0,就执行grow增长方法传递minCapacity参数
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
进行了一些列的比较与赋值,最后执行了Array.copyOf()方法 复制拷贝生成了新的 list对象,把对象添加了进去
1.3.2 Vector
案例:vector的常用方法(添加 删除与list都类似 下面仅写出特有的枚举器遍历)
public class test02_Vector {
public static void main(String[] args) {
Vector vector = new Vector();
vector.add("ONE");
vector.add("TWO");
vector.add("THREE");
//重点写一下vector的 枚举器遍历
Enumeration e = vector.elements();
while (e.hasMoreElements()){
System.out.println(e.nextElement());
}
}
}
1.3.3 LinkedList
用法与ArrayList基本一致,实现是用双向链表
案例:LinkedList源码
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
默认的容量为0, 一个头节点,一个尾节点
public boolean add(E e) {
linkLast(e);
return true;
}
add方法里面 调用了linklast方法
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
当添加一个元素到空链表
1.3.4 ArrayList 和 LinkedList区别
![](https://i-blog.csdnimg.cn/blog_migrate/9402407158133aed3f009106c82eecd9.png)
1.4 泛型
概念:泛型就是参数化类型,把类型作为参数传递
优点:提高代码的重用性,防止类型转换异常,提高代码的安全性
1.5 Set类
特点:无序,无下标,元素不重复
方法:全部继承于Collection中的方法
使用:基本方法于list类似,不可用fori 进行遍历
1.5.1 HashSet
存储结构:哈希表(数组+链表(+红黑树))
存储过程:1)根据对象的hashcode计算在数组中的保存位置,如果当前位置为空,直接保存,不为空执行下一步
2)不为空,执行equals方法,如果equals方法为true,会判断重复放弃保存,为false会形成链表
案例:HashSet基本使用,以及 通过重写Student的hashcode 和 equals方法 来判断元素一致性
public class test03_HashSet {
public static void main(String[] args) {
//1.新建一个HashSet 存入数据
HashSet<Student> hashSet = new HashSet<Student>();
Student s1 = new Student("西八哥",22);
Student s2 = new Student("王德发",32);
Student s3 = new Student("Lolita",18);
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
//2.打印目前数据,是无序的
System.out.println(hashSet.toString());
//3.通过重写hashcode和equals方法 来进行判定是否为一个数据对象
Student s4 = new Student("王德发",32);
hashSet.add(s4);
System.out.println(hashSet.toString());
//4.遍历 fori foreach 迭代器
//5.判断
}
}
重写的两个方法
@Override
public int hashCode() {
//1.获取到name的hashcode,内部已包装好 就是通过name去判断的
int n1 = this.name.hashCode();
//2.获取到age,简便就不获取hashcode的值了,已经可以进行唯一判断了
int n2 = this.age;
return n1+n2;
}
@Override
public boolean equals(Object obj) {
//1.判断是不是为同一个对象
if(this==obj){
return true;
}
//2.判断是否为空
if(obj==null){
return false;
}
//3.判断是否为Student类
if(obj instanceof Student){
Student s = (Student) obj;
//4.进行所需比较
if(this.getName()==s.getName() && this.getAge()==s.getAge()){
return true;
}
}
//5.都不满足
return false;
}
HashSet源码 其实HashSet里面用的就是一个HashMap
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
1.5.2 TreeSet
存储结构:红黑树
特点:1)基于排列顺序实现元素不重复
2)实现了SortedSet接口,对元素进行了排序
3)元素对象必须实现Compareable接口,指定排序规则
4)通过Compareto方法确定是否为重复元素
案例:进行添加重复元素,观察TreeSet结果
public class test03_TreeSet {
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<Student>();
Student s1 = new Student("西八哥",22);
Student s2 = new Student("王德发",32);
Student s3 = new Student("Lolita",18);
Student s4 = new Student("Lolita",18);
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
System.out.println(treeSet.toString());
}
}
注意:通过TreeSet存储引用类型,必须要实现Comparable的CompareTo方法
Student类中重写的CompareTo方法 (进行数据排序)
@Override
public int compareTo(Student o) {
int n1 = this.getName().compareTo(o.getName());
int n2 = this.getAge()-o.getAge();
return n1==0?n2:n1;
}
对于实现Comparable接口的另一种实现方法,在创建TreeSet的时候,使用Compartor 匿名内部类直接重写CompareTo方法
TreeSet<Student> treeSet = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int n1 = o1.getName().compareTo(o2.getName());
int n2 = o1.getAge()-o2.getAge();
return n1==0?n2:n1;
}
});
案例:通过TreeSet判定字符串的大小
public class test03_TreeSetDemo {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<String>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int n1 = o1.length()-o2.length();
int n2 = o1.compareTo(o2);
return n1==0?n2:n1;
}
});
treeSet.add("helloworld");
treeSet.add("pingguo");
treeSet.add("lisi");
treeSet.add("zhangsan");
treeSet.add("beijing");
treeSet.add("cat");
System.out.println(treeSet.toString());
}
}
二、Map集合类
![](https://i-blog.csdnimg.cn/blog_migrate/dae212cb15be122f424fdf30940b3e3d.png)
特点:1)存储任意键值对,key-value
2)键 :无序、无下标、不可重复
3)值:无序、无下标、可重复
4)如果存入相同的key,且识别为同一个对象,那对应的value会被新的value覆盖
案例:使用Map 进行数据增加 删除 和 遍历
public class test04_Map {
public static void main(String[] args) {
//1.Create a new map
Map<String,String> map = new HashMap<String,String>();
//2.Add data to map
map.put("one","XBOX");
map.put("two","PS5");
map.put("three","SWITCH");
map.put("four","SWITCH");
System.out.println(map.toString());
//3.Delete data
map.remove("two");
System.out.println(map.toString());
//ps: If the same key,the value will be covered
map.put("four","SWITCHHHHHHHHHHHH");
//4.Print map - two way - keySet - EntrySet
//keySet
System.out.println("--------------By Key Set-----------------");
Set<String> keySet = map.keySet();
for (String key:keySet) {
System.out.println(key+"--->"+map.get(key));
}
//EntrySet
System.out.println("--------------By EntrySet-----------------");
for (Map.Entry<String,String> set:map.entrySet()) {
System.out.println(set.getKey()+"---->"+set.getValue());
}
}
}
2.1 HashMap
特点:线程不安全,运行效率快,允许使用null作为key、或者value
案例:使用Student类作为key , String类型作为value进行HashMap的操作
public class test04_HashMap {
public static void main(String[] args) {
//1.Create a new HashMap, key is Student - value is String
HashMap<Student,String> hashMap = new HashMap<>();
hashMap.put(new Student("一花",20),"老大");
hashMap.put(new Student("二乃",20),"老二");
hashMap.put(new Student("三玖",20),"老三");
System.out.println(hashMap.toString());
//2.override hashcode and equals method to distinct the data
hashMap.put(new Student("三玖",20),"忙内");
System.out.println(hashMap.toString());
//3. Delete、Print、ergodic、the same with Map
}
}
注意:如果想要程序把新的Student对象 识别为已有,需要重写Student类的hashcode和equals方法
案例:Has和Map源码分析
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 ---默认初始化大小
static final int MAXIMUM_CAPACITY = 1 << 30; ----最大的容量
static final float DEFAULT_LOAD_FACTOR = 0.75f; ---默认加载因子,到总大小的75%进行扩容
static final int TREEIFY_THRESHOLD = 8; ---当链表长度大于8,转换为红黑树
static final int UNTREEIFY_THRESHOLD = 6; ---当链表长度小于6 转化为链表
static final int MIN_TREEIFY_CAPACITY = 64; ---数组长度大于链表长度大于8,集合元素大于64转换红黑树
transient Node<K,V>[] table; ---哈希表中的 数组
transient int size; ---元素个数
put方法源码
//1.调用putVal方法-->传对应值进去
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
//2.进行数组resize,resize完毕后,把当前节点放入对应位置
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//3.resize完后,返回resize完后大小的数组
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
//中间代码省略
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
//4.resize的扩容,当到达装载因子的边界,会进行扩容,是原来的两倍
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
过程解析:
1)HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量调整为16
2)当元素个数大于(16*0.75=12时),会进行扩容,扩容后会变为原来的2倍
3)JDK1.8之前是头插法,1.8之后是尾插法
2.2 TreeMap
特点:实现了SortedMap接口,可以对key自动排序,泛型对象要实现Comparable接口 同TreeSet
三、Collections工具类
案例:Collections工具类的方法使用
public class test05_Collections {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(156);
list.add(121);
list.add(1);
//sort
System.out.println(list.toString());
Collections.sort(list);
System.out.println(list.toString());
//binarySearch return index
System.out.println(Collections.binarySearch(list,156));
//copy ps:the length must same
List<Integer> list2 = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
list2.add(0);
}
Collections.copy(list2,list);
System.out.println(list2.toString());
//reverse
Collections.reverse(list);
System.out.println(list.toString());
//shuffle break the order
Collections.shuffle(list);
System.out.println(list.toString());
//list convert to array
Integer[] array = list.toArray(new Integer[10]);
System.out.println(Arrays.toString(array));
//array convert to list
String[] names = {"A","B","C"};
List<String> list3 = Arrays.asList(names);
System.out.println(list3.toString());
//ps: as int、byte type data need to use Integer、Byte
Integer[] nums = {3,2,45};
List<Integer> list4 = Arrays.asList(nums);
System.out.println(list4.toString());
}
}