集合框架
为什么使用集合框架?
1、数组长度是固定的
2、数组无法同时存储多个不同的数据类型
集合就是一个长度可变,可以保存任意数据类型的动态数组。
Java种的集合不是由一个类来完成的,而是由一组接口和类构成了一个框架体系,大致可分为3层,最上层是一组接口,再而是接口的实现类。
接口
Collection: 集合框架最基础的接口,
List:Collection的子接口,有序不唯一,最常用的接口
Set: Collection的子接口,存储无序、唯一的对象
Map:独立于Collection的另外一个接口,最顶层的接口, 存储一组简直对象,提供键到值的映射
Iterator:输出集合元素的接口,一般适用于无序集合,从前往后输出
ListLterator:lterator子接口,可以双向输出集合种的元素。
SortedMap:Map的子接口,可以对集合种的元素进行排序。
SortedSet:Set的子接口,可以对集合种的元素进行排序。
Queue:队列接口
Map.Entry:Map内部接口,描述Map种存储的一组键值对元素。
Enumeration:传统的输出接口,已经被lterator取代
Collection 接口
Collection 是集合框架种最基础的父接口,可以存储一组无序,不唯一的对象。
* @since 1.2
*/
public interface Collection<E> extends Iterable<E> {
// Query Operations
可以存储一组无序,不唯一的对象,一般不直接使用该接口,也不能被实例化,只是用来提供规范。
Collection是lterable接口。
int size() | 获取集合长度 |
---|---|
boolean isEmpty() | 判断集合是否为空 |
boolean contains(Object o) | 判断集合种是否存在某个对象 |
lteratorlterator() | 实例化lterator接口,遍历集合 |
Object[] toArray | 将集合转换为一个Object数组 |
T[] toArray(T[ ] a) | 将集合转换为一个指定数据类型的数组 |
boolean add(E e) | 向集合添加元素 |
boolean remove(Object o) | 从集合中删除某元素 |
boolean containsAll | 判断集合种是否存在另一个集合的所有元素 |
boolean addAll(Collection c) | 向集合种添加某个集合的所有元素 |
boolean removeAll(Colletion c) | 从集合种删除某个集合的所有元素 |
void clea() | 清除集合中的所有元素 |
boolean equals(Collection c) | 判断两个集合是否相等 |
int hashCode() | 返回集合的哈希值 |
Collection子接口
- List: 存放有序,不唯一的元素
- Set: 存放无序,唯一的元素
- Queue: 队列接口
List接口
* @since 1.2
*/
public interface List<E> extends Collection<E> {
// Query Operations
List 常用方法
T get(int index) | 通过下标返回集合种对应位置的元素 |
---|---|
T set(int index,T element) | 在集合中的指定位置存入对象 |
int indexOf(Object o) | 从前向后查找某个对象在集合中的位置 |
int lastIndexOf(Object o) | 从后向前查找某个对象在集合中的位置 |
Listlteratorlistlterator() | 实例化Listlterator接口,用来遍历List集合 |
ListsubList(int fromIndex,int toIndex) | 通过下标截取List集合 |
List接口的实现类
ArrayList 是开发中使用频率最高的List实现类,实现长度可变的数组,在内存中分配连续空间,所有读取快,增删慢
import java.util.ArrayList;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("hello");
arrayList.add("world");
arrayList.add("JavaSE");
arrayList.add("JavaME");
arrayList.add("JavaEE");
System.out.println("ArrayList: "+arrayList);
System.out.println("ArrayList长度: "+arrayList.size());
System.out.println("ArrayList是否包含Java: "+arrayList.contains("Java"));
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
arrayList.remove("hello");
arrayList.remove(0);
System.out.println("***********");
System.out.println(arrayList);
arrayList.add(1,"Spring");
System.out.println(arrayList);
arrayList.add(1,"SpringBoot");
System.out.println(arrayList);
//改 set
arrayList.set(1,"Spring Could");
System.out.println(arrayList);
System.out.println("*********************************");
//根据元素返回下标
System.out.println(arrayList.indexOf("Spring"));
//根据区间值截取元素
System.out.println(arrayList.subList(1,3));
}
}
ArrayList: 基于数组的实现,非线程安全,效率高,所有方法都没有synchronized修饰
Vector:线程安全,效率低,实现线程安全直接通过synchronized修饰方法来完成。
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
for (int i = 0; i < 10; i++) {
final int temp = i;
new Thread(() -> {
try {
TimeUnit.MICROSECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
arrayList.add(String.valueOf(temp));
System.out.println(arrayList);
}).start();
}
}
}
java.util.ConcurrentModificationException
并发修改异常
解决异常
public class Test3 {
public static void main(String[] args) {
Vector arrayList = new Vector();
for (int i = 0; i < 10; i++) {
final int temp = i;
new Thread(() -> {
try {
TimeUnit.MICROSECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
arrayList.add(String.valueOf(temp));
System.out.println(arrayList);
}).start();
}
}
}
Stack: Vector的子类,实现了栈的数据结构 先进后出
- push: 入栈
- peek: 输出栈顶元素,但不删除元素,取完后栈内数据不变
- pop: 输出栈顶元素,并删除,取完之后栈内的数据减一
LikedList: 实现了先进先出的队列,采用链表的形式来存储。
ArrayList和LikedList区别: 都是线程不安全
在内存中存储形式不同,ArrayList采用的数组方式,LinkedLiss采用的是链表的形式。
数组在内存中存储空间是连续的,读取快,增删慢
因为数组在内存中是连续的,所以取数据可以通过寻址公式很快求出目标元素的内存地址,因为内存是连续的,所以新增或者删除元素,必须需要移动数据,而且数组长度越长,需要移动的元素越多,操作就越慢。
链表在内存中存储空间是不连续的,读取慢,增删快。链表在内存中是不连续的,没有固定的公式可以使用,要读取只能从第一位开始一直遍历到目标元素,数据规模越大,操作越慢。
增删快,因为只需要重新设置目标元素前后两个节点的后置指针即可,与数据规模无关。
import java.util.LinkedList;
public class Test5 {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add("Hello");
linkedList.add("world");
linkedList.add("JavaEE");
System.out.println(linkedList);
linkedList.offer("JavaSE");
System.out.println(linkedList);
linkedList.push("Spring");
System.out.println(linkedList);
linkedList.addFirst("First");
System.out.println(linkedList);
linkedList.addLast("last");
System.out.println(linkedList);
System.out.println(linkedList.peek());
System.out.println(linkedList.peekFirst());
System.out.println(linkedList.peekLast());
System.out.println(linkedList.pop());
System.out.println(linkedList);
}
}
LinkedList和Stack都有pop方法,有什么区别和相同点
pop方法都是取出集合中第一个元素,但两者顺序是相反的,Stack是”先进后出“,所以pop取出的是最后一个元素,LikedList是”先进先出“,所以pop取出的第一个元素。
LikedList实现了Deque接口,而Deque接口是Queue的子接口,Queue就是队列,底层实现了队列的数据结构。
实际开发中,不能直接实例化Queue对象。
Queue的实现类是AbstractQueue,它是一个抽象类,不能直接实例化,开发中需要实现它的子类PriorityQueue.
Queue中添加的数据必须是有顺序的。
public class Test6 {
public static void main(String[] args) {
PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add(new A(1));
priorityQueue.add(new A(2));
System.out.println(priorityQueue);
}
}
class A {
private int num;
public A(int num) {
this.num = num;
}
}
Exception in thread “main” java.lang.ClassCastException:
类型转换异常
解决
import java.util.PriorityQueue;
public class Test6 {
public static void main(String[] args) {
PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add(new A(1));
priorityQueue.add(new A(2));
System.out.println(priorityQueue);
}
}
class A implements Comparable {
private int num;
public A(int num) {
this.num = num;
}
@Override
public int compareTo(Object o) {
A a =(A)o;
if(this.num > a.num){
return 1;
}else if(this.num == a.num) {
return 0;
}else {
return -1;
}
}
@Override
public String toString() {
return "A{" +
"num=" + num +
'}';
}
}
Queue默认给元素进行升序排序,既自然排序。
List Set Map 的 区 别
- List:
- Set:
- Map:
List Set: 存储的是单个数据,List可以存储重复的数据,Set数据不能重复
Map: 存储的是一组数据 是键值对的形式存在
Set
跟List一样,Set是Collection的子接口,Set集合是以散列的形式存储数据,元素是没有顺序的,可以存储一组无序且唯一的数据
public interface Set<E> extends Collection<E> {
// Query Operations
常用实现类:
- HashSet
- LinkedHashSet
- TreeSet
HashSet是开发中经常使用的一个实现类,存储一组无序且唯一的对象。
无序:元素的存储顺序和遍历顺序不一致。
import java.util.HashSet;
import java.util.Iterator;
public class Test2 {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add("Hello");
set.add("Java");
set.add("world");
set.add("Hello");
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("------------------------------");
set.remove("world");
iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
LikedHashSet是Set的另外一个实现类,可以存储一组有序且唯一的元素。
有序:元素的存储顺序和遍历顺序一致。
equals 和 == 区别
所有类中的equals都是继承Object,Object类中原生的equals方法就是在通过 == 进行判断
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
但是每个类都可以对equals方法进行重写,覆盖掉之前使用 == 进行判断的逻辑,改用新的逻辑进行判断是否相等。
LinkedhashSet如何判断两个对象是否相等?
首先会判断两个对象的hashCode是否相等
什么是hashCode?
将对象的内部信息(内存地址、属性值等),通过某种特定规则转成一个散列值,就是该对象的hashCode.
- 两个不同对象的HashCode值可能相等。
- hashCode不相等的两个对象一定不是同一个对象。
集合在判断两个对象是否相等的时候,会先比较他们的HashCode,如果HashCode不相等,则认为不是同一个对象,可以添加。
如果HashCode值相等,还不能认为两个对象是相等,需要通过equals进行进一步的判断,equlas相等,则两个对象相等,否则两个对象不相等。
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
public class Test3 {
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet();
Data data1 = new Data(1);
set.add(data1);
Data data2 = new Data(1);
set.add(data2);
System.out.println(data1.equals(data2));
System.out.println(set);
}
}
class Data{
private int num;
public Data(int num) {
this.num = num;
}
@Override
public String toString() {
return "Data{" +
"num=" + num +
'}';
}
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
//instanceof 判断对象是否属于某个类
if( obj instanceof Data){
Data data = (Data) obj;
if(this.num == data.num);
return true;
}
return false;
}
@Override
public int hashCode() {
return 1;
}
}
== 判断的是栈内存中的值
引用类型的数据,栈内存中存储的是地址,所以此时 == 判断的是引用地址 。
基本数据类型,栈内存中存储的是具体的数值。
栈中存储的是变量
Data data;
引用类型具体的对象(属性)存储在堆中,再将堆中对象的内存地址赋值给栈中的变量data,data中存储的就是地址。
基本数据类型不需要用到堆内存,变量在栈中,变量的值直接存储在变量中。
TreeSet
LinkedHashSet和TreeSet都是存储一组有序且唯一的数据,但是这里的两个有序是有区别的。
LinkedHashSet的有序是指元素的存储顺序和遍历顺序一致的。
TreeSet的有序是指集合内部会自动对所有元素按照升序进行排列,无论存入的顺序是什么,遍历的时候一定按照升序输出。
import java.util.Iterator;
import java.util.TreeSet;
public class Test5 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(1);
treeSet.add(5);
treeSet.add(7);
treeSet.add(3);
treeSet.add(4);
treeSet.add(1);
treeSet.add(2);
System.out.println("treeSet长度: "+treeSet.size());
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Exception in thread “main” java.lang.ClassCastException:
类型不匹配异常
import java.util.TreeSet;
public class Test6 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new Data0(1));
treeSet.add(new Data0(3));
treeSet.add(new Data0(4));
treeSet.add(new Data0(5));
System.out.println("treeset长度 "+ treeSet.size() );
}
}
class Data0{
private int num;
public Data0(int num) {
this.num = num;
}
}
解决
import java.util.Iterator;
import java.util.TreeSet;
public class Test6 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new Data0(1));
treeSet.add(new Data0(3));
treeSet.add(new Data0(4));
treeSet.add(new Data0(5));
treeSet.add(new Data0(2));
System.out.println("treeSet长度 "+ treeSet.size() );
System.out.println("treeSet遍历 ");
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
class Data0 implements Comparable{
private int num;
public Data0(int num) {
this.num = num;
}
/**
* A.comparable
* 返回值
* 1 表示A 大于 B
* 0 表示A 等于 B
* -1 表示A 小于 B
* @param o
* @return
*/
@Override
public int compareTo(Object o) {
if ( o instanceof Data0){
Data0 data0 = (Data0) o;
if(this.num > data0.num){
return -1;
}else if(this.num == data0.num){
return 0;
}else {
return 1;
}
}
return 0;
}
@Override
public String toString() {
return "Data0{" +
"num=" + num +
'}';
}
}
Map
List、Set接口都是Collection的子接口,Map接口是与Collection完全独立的另外一个体系。
List & Set Vs Map
List & Set & Collection只能操作单个元素,Map可以操作一对元素,因为Map存储结构是Key- Value映射。
Map接口定义时使用泛型,并且定义两个泛型K和V,K表示key,规定键元素的类型,V表示value,规定值元素的类型。
方法 | 描述 |
---|---|
int size() | 获取集合长度 |
boolean isEmpty() | 集合是否为空 |
boolean containsKey(Object key) | 判断集合中是否存在某个key |
boolean containsValue(Object value) | 判断集合中是否存在某个Value |
V get(Object key) | 取出集合中key对应的value |
V put(Object key) | 向集合中存入一组key-value的元素 |
V remove(Object) | 删除集合中key对应的value |
void purAll(Map map) | 向集合中添加另外一个Map |
void clear() | 清除集合中所有的元素 |
Set keySet() | 取出集合中所有的key,返回一个Set |
Collectionvalues() | 取出集合中所有的Value,返回一个Collection |
Set<Map.Entry<K,V>entrySet() | 将Map以Set的形式输出 |
int hashCode() | 获取集合的散列值 |
boolean equals(Object o) | 比较两个集合是否相等 |
Map接口的实现类
- HashMap: 存储一组无序,key不能重复,value可以重复的元素。
- Hashtable: 存储一组无序的,key不可以重复,value可以重复的元素。
- TreeMap: 存储一组有序,key不可以重复,value可以重复的元素,可以按照key进行排序。
HashMap常规操作
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.locks.Condition;
public class Test7 {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("h","hello");
hashMap.put("j","java");
hashMap.put("a","apple");
hashMap.put("c","cat");
hashMap.put("e","javaee");
System.out.println(hashMap);
hashMap.remove("h");
System.out.println("删除之后"+hashMap);
hashMap.put("j","hhhhhhhhhhhh");
System.out.println("添加之后"+hashMap);
if(hashMap.containsKey("a")){
System.out.println("集合中存在key = a");
}else {
System.out.println("集合中不存在key = a");
}
if(hashMap.containsValue("cat")){
System.out.println("集合中存在value=cat");
}else {
System.out.println("集合中不存在value=cat");
}
Set keys = hashMap.keySet();
System.out.println("集合中的key");
Iterator iterator = keys.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("集合中的Value");
Collection values = hashMap.values();
for (Object value : values) {
System.out.println(value);
}
System.out.println("****************************");
iterator = keys.iterator();
while (iterator.hasNext()){
String key = (String) iterator.next();
String value = (String) hashMap.get(key);
System.out.println(key +" - "+value);
}
}
}
*iterator 只取不收回 ,所有后面 在使用需重写赋值
Hashtable用法与HashMap基本一样,区别是Hashtable是线程安全的性能较低,HashMap是非线程安全的,但性能较高。
HashMap,方法没有用synchronized修饰,所有非线程安全
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
Hashtable,方法被synchronized修饰,所以是线程安全的
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
Hashtable的使用
package com.xmm.maptest;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Set;
/**
* @Description TODO
* @Author Xm
* @Date 2022/8/3 22:15
*/
public class Test {
public static void main(String[] args) {
Hashtable hashtable = new Hashtable();
hashtable.put("h","hello");
hashtable.put("j","java");
hashtable.put("p","put");
hashtable.put("c","cat");
hashtable.put("m","move");
hashtable.put("f","fun");
System.out.println(hashtable);
hashtable.remove("f");
System.out.println(hashtable);
System.out.println(hashtable.containsKey("f"));
System.out.println(hashtable.containsValue("cat"));
Set keys = hashtable.keySet();
System.out.println(keys);
Collection values = hashtable.values();
System.out.println(values);
}
}
HashMap和HashTable,保存的数据都是无序的,Map 的另一个实现类TreeMap主要功能是按照key对集合中的元素进行排序。
TreeMap使用
import javax.jws.soap.SOAPBinding;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
public class Test4 {
public static void main(String[] args) {
TreeMap treeMap = new TreeMap();
treeMap.put(new User(3, "java"), "java");
treeMap.put(new User(5, "javaME"), "javaME");
treeMap.put(new User(2, "javaEE"), "javaEE");
treeMap.put(new User(1, "javaCC"), "javaCC");
treeMap.put(new User(4, "javaBB"), "javaBB");
treeMap.put(new User(6, "javaSE"), "javaSE");
System.out.println(treeMap);
Set keys = treeMap.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + treeMap.get(key));
}
}
}
class User implements Comparable {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
if (this.id > user.id) {
return 1;
} else if (this.id == user.id) {
} else {
return -1;
}
}
return 0;
}
}
Collections 工具类
Collection 接口,List和Set的父接口。
Collection不是接口,他是一个工具类,专门提供了一些对集合的操作,方便开发者去使用,完成相应的业务功能。
Collections针对于集合的工具类, Collection
Arrays针对数组的工具类,Array
方法 | 描述 |
---|---|
public static sort() | 对集合进行排序 |
public static int binarySearch(List list,Objetc v) | 查找v在list的位置,集合必须是升序排列 |
public static get(List list,int index) | 返回list中index位置的值 |
public static void reverse(List list) | 对list进行反序输出 |
public static void swap(List list,int i, int j) | 交换集合中指定位置的两个元素 |
public static void fill(List list,Object obj) | 将集合中所有元素替换成 obj |
public static Object min(List list) | 返回集合中最小值 |
public static Object max(List list) | 返回集合中最大值 |
public static boolean replaceAll(List list,Object old,Object new) | 在list集合中用new替换old |
public static boolean addAll(List list,Object…obj) | 向集合中添加元素 |
可变参数 |
public static void test(int ... arg){
}
可以传任意类型,任意数量的参数,多态的一种体现
public static void test(Object ... arg){
}
Java中默认输出对象的格式:对象所属的全类名(全限定类名)带着包名的类名+@+对象的哈希值