Java 集合
1. 集合概念
1.1 集合的定义
集合与数组类似,用来存储一系列数据或元素。
1.2 集合和数组的区别
- 长度区别:数组长度是固定的,集合长度是可以改变的;
- 类型区别:数组可以存储基本数据类型和引用数据类型,集合只能存储引用数据类型;
- 内容区别:数组只能存储同一种类型,集合可以存储不同的类型;
1.3 集合框架图
发现一个特点,上述所有的集合类,除了map系列的集合,即左边的集合都实现了Iterator接口。
Iterator是一个用来遍历集合中元素的接口,主要有hashNext(),next(),remove()三种方法。
它的子接口ListIterator在它的基础上又添加了三种方法,分别是add(),previous(),hasPrevious()。
从图中我们可以看到:
1.集合主要分为Collection和Map两个接口。
2.Collection又分别被List和Set继承。
3.List被AbstractList实现,然后分为3个子类,ArrayList,LinkedList和VectorList。
4.Set被AbstractSet实现,又分为2个子类,HashSet和TreeSet。
5.Map被AbstractMap实现,又分为2个子类,HashMap和TreeMap。
6.Map被Hashtable实现。
1.4 主要学习框架
集合重点学习内容:
1.5 List、Set和Map的区别
集合 | 是否有序 | 能否重复 |
---|---|---|
List | 有序 | 能 |
Set | 无序 | 不能 |
Map | 无序 | 双列型,key不能,value可以 |
2. List 集合
2.1 ArrayList
底层是数组。
数组可以存储不同的数据类型,在创建这类数组时,需要用祖先类Object。
以下是创建和查询一个集合的代码实例:
import java.util.ArrayList;
public class Demo1 {
public static void main(String[] args) {
Object[] obbattr = {true,'好',12,3.14F};
//Object 创建的数据都是封装类型,obbattr数组里的类型分别是:Boolean、Character、Integer、Float
// 集合是可以存放饮用类型
ArrayList aList = new ArrayList(); //集合默认大小为10,但是可以随着元素个数自增
aList.add(true);
aList.add('好');
aList.add(12);
aList.add(3.14F);
aList.add(true);
aList.add('好');
aList.add(12);
aList.add(3.14F);
// 打印单个元素
Object a = aList.get(0); //访问单个集合元素用get,下标是从0到元素个数-1,越界会报异常
System.out.println(a);
// 遍历集合有三种方式
// 1.传统for循环
for(int i=0;i<aList.size();i++){ //数组长度是length,集合是size
System.out.println(aList.get(i));
}
// 2.高级for循环(jdk1.5版本的新功能)
for(Object temp : aList){ //数组长度是length,集合是size
System.out.println("temp="+temp);
}
// 3.lambda表达式(jdk1.8版本的新功能,且只能遍历集合)
aList.forEach(temp->{ //无需指定temp类型,会根据集合内元素的类型自动识别
System.out.println(temp);
});
}
}
泛型: 指的是在设计程序时可以自定义某些可变部分,在使用这部分之前需要声明。
代码实例:
import java.util.ArrayList;
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> bList = new ArrayList(); //简写
ArrayList<String> cList = new ArrayList<>(); //简写
ArrayList<String> dList = new ArrayList<String>(); //标准写法
bList.add("张三");
bList.add(4); //报错,若想装4,需要指定为Integer类型
}
}
泛型可以指定集合只能存储单个数据类型,但是指定的类型不能是基本类型,只能是引用类型(例如:String、Integer、Character等)。
以下是删除和修改一个集合的代码实例:
import java.util.ArrayList;
public class Demo3 {
public static void main(String[] args) {
Object[] obbattr = {true,'好',12,3.14F};
ArrayList aList = new ArrayList(); //集合默认大小为10,但是可以随着元素个数自增
aList.add(true);
aList.add('好');
aList.add(12);
aList.add(3.14F);
aList.add(true);
aList.add("好");
aList.add(12);
aList.add(3.14F);
//删除单个元素有两种方法:按下标和按内容
//1.按下标
aList.remove(0);
//2.按内容
aList.remove("好"); //按内容删除只能删除String类型的元素,删不掉Character类型的元素
//清空集合元素
aList.clear();
//修改(格式:下标,要修改的结果(在没有使用泛型时,可以改为任意类型的值,使用泛型后要遵守约定))
aList.set(0,222222); //修改下标为0的元素,改为222222
aList.forEach(temp->{
System.out.println(temp);
});
}
}
由于remove默认为按下标删除。若一个集合是纯数字集合,要按照内容删除,则:
lists.remove((Object)99);可以确保是按内容删除,删除内容为99的元素。
2.2 LinkedList
底层是链表。
LinkedList的使用方法,包括增删改查与ArrayList一模一样。
LinkedList 和 ArrayList的区别:
集合 | ArrayList | LinkedList |
---|---|---|
底层 | 基于数组 | 基于链表 |
查询速度 | 快 | 慢 |
增删速度 | 慢 | 快 |
关于查询速度和增删速度的比较快慢的解释:
ArrayList基于数组,而数组存储的地址空间是连续的,所以ArrayList查询速度快。而LinkedList基于链表,链表在内存中的存储空间是不连续的,只能从头元素开始逐个访问下一个元素的地址。所以数组查询快。
而对于增加和删除的操作,数组中每增加一个元素都会影响后面元素的下标。
集合可以根据需要转数组:
一般转换:OBject Arr = aList.toArray();
强制转换:String Arr = (String[])aList.toArray();
集合可以装入另一个集合:
aList.addAll(bList);
3. Set集合
3.1 HashSet
底层是哈希码。
增加代码演示:
import java.util.HashSet;
public class Demo1 {
public static void main(String[] args) {
HashSet<String> aList = new HashSet();
aList.add("a");
aList.add("12345678");
aList.add("0");
aList.add("0"); //虽然Set不允许重复,但是程序不会报错
aList.add("我爱你");
aList.forEach(temp->{
System.out.println(temp);
});
}
}
Set声明与增加方法和List类似,但是由于Set是无序不可重复的。
所以结果顺序是不确定的,以下是其中一种结果:
0
a
我爱你
12345678
若添加的有相同的值,Set会自动去重,不会报错。
3.2 删除、修改和查询
删除时,由于底层是哈希码,没有索引值,所以只能用remove(“内容”)的方法进行删除。
修改时,由于没有索引值无法修改,所以只能是删除再添加。
查询时,由于没有索引值无法查询单个,所以只能遍历整个集合。
例如:
aList.add("0");
修改 0 为1
则:
aList.remove("0");
aList.add("1");
3.3 TreeSet
底层是二叉树。
Set是不可重复无序,但是TreeSet是不可重复有序的。
因为TreeSet实现了 Comparable 或 Comparator 接口。
Java的八种基本数据类型的包装类都实现了 Comparable 接口,所以可以排序(默认从小到大)。
但是对字符串类String集合排序仅限英文(按首字母顺序,中文不能排序)。
3.4 增加、删除、修改和查询
增加方式依然是add()和addAll()。
删除时,由于底层是二叉树,没有索引值,所以只能用remove(“内容”)的方法进行删除。
修改时,由于没有索引值无法修改,所以只能是删除再添加。
查询时,由于没有索引值无法查询单个,所以只能遍历整个集合。
4. Map集合
Map是双列集合。每条数据都有一个 key 值和一个 value 值, key 唯一。
4.1 HashMap
底层是哈希码。
与List不同,HashMap添加数据时使用的是put方法。
实例:
import java.util.HashMap;
public class Demo4 {
public static void main(String[] args) {
HashMap<Integer,String> bookMap = new HashMap<>();
//1.添加
bookMap.put(1,"第一本书");
bookMap.put(1,"第二本书"); //key不允许重复,所以会覆盖掉上一个
String v1 = bookMap.put(1,"第三本书");
bookMap.put(2,"第四本书");
System.out.println("返回值是:"+v1); //返回值是刚刚被覆盖掉的value
//2.查询
//2.1 通过 key 查询单个 value
String v2 = bookMap.get(1);
System.out.println("key为1的值是:"+v2);
//2.2 遍历Map 用lambda表达式
bookMap.forEach((k,v)->{
System.out.println(k+"="+v);
});
//2.3 直接打印Map集合,因为HashMap中已经覆盖了toString()方法
System.out.println(bookMap);
//3.修改 思路是直接添加,会覆盖掉
// bookMap.put(2,"第五本书");
// 4.删除
// 4.1 删除单个 根据key删value
bookMap.remove(1);
System.out.println(bookMap);
// 4.1 删除全部
bookMap.clear();
System.out.println(bookMap);
}
}
注意: 返回值返回的都是被覆盖掉的旧值,若没有旧值,则返回null。
4.2 TreeMap
底层是二叉树。
可以根据Key进行排序。前提是Key类型为数字或者字母等。
假如有一个字符串,求各个字符出现的次数。
代码如下:
import java.util.TreeMap;
public class Demo5 {
public static void main(String[] args) {
//给定一个字符串:String str,统计每个字符出现的次数
//提示:TreeMap<String,Integer>
String str = "askqweasdasqwefasasf";
//1.将字符串转换为字符数组
char[] chs = str.toCharArray();
//2.取出chs中的元素,看容器中是否存在
TreeMap<Character,Integer> aMap = new TreeMap<>();
for(char temp:chs){
Integer value = aMap.get(temp);
if(value==null){
aMap.put(temp,1);
}else{
value++;
aMap.put(temp,value);
}
}
//打印结果
aMap.forEach((k,v)->{
System.out.println(k+"="+v);
});
}
}