目录
集合和数组
数组(Arrays)和集合其实本质上没啥区别,都是容器,用来存放数据罢了。
那为什么有了数组还要使用集合呢?为啥苹果公司发布了苹果4之后还要发布苹果5呢?
肯定是实现了功能的升级呀!
集合相对数组的优点
数组 | 集合 |
---|---|
长度固定,不能扩容 | 自动扩容 |
不面向对象 | 面向对象 |
无法判断实际存储了多少元素(length函数是数组的总长度) | 可以判断实际存储元素的数量 |
仅采用顺序表方式 | 有多种实现方式来满足不同的场景 |
存储类型固定 | 存储类型不固定 |
集合的特点
- 不需要指定集合长度,自动扩容
- 存储的类型不固定
- 不能存储基本类型,输入int,集合会自动转换为Integer
家谱图
- Java的集合框架中有两个基本的集合接口:Collection、Map
- Collection继承Iterable接口,这个接口允许对象成为 “foreach” 语句的目标
- Collection有几个比较实用的子接口:有序、可以包含重复元素的列表List,无序、不包含重复元素的Set,有序、可以包含重复元素、先进先出、不可随机访问的Queue
- Map有几个比较实用的子接口:线程不安全、允许存储null的HashMap,线程安全、不允许存储null的Hashtable,使用二叉树的算法实现键值的自然排序功能的TreeMap
PS
- 有序与无序的实质:即存入数据的顺序和取出的顺序。例如:存入数据的顺序1234,取出的顺序:1234,这样的情况即有序
- 无序不等于随机,而是按照哈希算法或者排序之后来决定存储数据的位置
泛型
因为集合的存储是不限类型的,integer类型、float类型都可以能放进同一个集合,所以存在潜在风险,鱼龙混杂,不易分类。
例如:一个普通班级,无论成绩好坏的学生都可以进入
代码:Collection 普通班 = new ArrayList();
泛型是指定某个集合都是某种类型
例如:有一个班级叫尖子班,只有尖子生才能进入
代码:Collection 尖子班<尖子生> = new ArrayList<尖子生>();
List
特点:有序,存储的元素可重复。
List常用的实现类有ArrayList、Vector和LinkedList三个,都实现了List接口,所以它们的操作方法都是相同的,区别在于它们在底层上的实现方式不一样,ArrayList和Vector都是基于数组来存储元素的,LinkedList是基于链表方式存储元素的。
- ArrayList:数组实现方式,随机查询速度快,增删速度慢,线程不安全,效率高
- LinkedList:链表实现方式,随机查询速度慢,增删速度块,线程不安全,效率高
- Vector:数组实现方式,随机查询速度快,增删速度慢,线程安全,效率低
ArrayList常用方法
增:add、addAll
删:remove、removeAll、clear
查:indexOf、lastIndexOf、get、contains
改:set
//初始化ArrayList变量
ArrayList<String> aL = new ArrayList<String>();
增
//将指定的元素添加到此列表的尾部
aL.add("123");
//将指定的元素插入此列表中的指定位置
aL.add(0, "000");
//定义新的集合
ArrayList<String> aL1 = new ArrayList<String>();
//把一个集合的全部元素加入此列表的尾部
aL.addAll(aL1);
删
//移除此列表中指定位置上的元素。
aL.remove(1);
//移除此列表中首次出现的指定元素
aL.remove("123");
//移除此列表中包含其他集合的元素
aL.removeAll(aL1);
//移除此列表中的所有元素
aL.clear();
查
//返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
System.out.println(aL.indexOf("1"));
//返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
System.out.println(aL.lastIndexOf("123"));
//如果此列表中包含指定的元素,则返回 true。
System.out.println(aL.contains("123"));
// 返回此列表中指定位置上的元素
System.out.println(aL.get(0));
改
//用指定的元素替代此列表中指定位置上的元素
aL.set(2, "789");
集合元素的数量
// 如果此列表中没有元素,则返回 true
System.out.println(aL.isEmpty());
//返回此列表中的元素数
System.out.println(aL.size());
LinkList常用方法
提供方法表,不再代码演示,接下来也是如此。
Vector常用方法
Set
特点:无序、无重复的元素。
Set接口也有很多的实现类,常用的有两种:
-
HashSet:底层数据结构是哈希表、通过hashCode()和equals()保证元素的唯一性(自定义类需要重写两个方法才能确保不重复)
-
TreeSet:通过红黑树结构来对元素进行排序(默认升序),排序比较如果相同那么就会排除重复元素,来保证Set的元素唯一特性。
HashSet
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。(先比较hashCode再比较equals)
HashSet集合判断两个元素相同的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。
HashSet的方法如下:
HashSet的子类如下:
- LinkedHashSet底层数据结构是链表和哈希表,由链表保证元素有序,由哈希表保证元素唯一。
LinkedHashSet
LinkedHashSet本质上算拼接类,集百家之长。
它的方法来自于HashSet、Set、Collection、Object、Set,详细方法如下。
TreeSet
- TreeSet有两种排序方法:自然升序排序、实现Comparable接口(自定义类需要重写hascode、equals方法)
TreeSet的方法如下:
Queue
特点:有序、可重复、先进先出、不允许有null
详细方法如下:
Map
特点:根据K取出V,K是唯一的,无法根据V取出K
PS:K为Key,V为Value,Map由KV组成,俗成键值对。
对于Map接口是有多种实现方式的子类:
- HashMap,按照散列存储,这样的存取较快,线程不安全的,允许存放null键,null值。
- Hashtable,线程安全,速度慢,不允许存放null键,null值。
- TreeMap,使用二叉树的算法来实现键值的自然排序功能。
如果判断的对象是自定义的对象,需要正确重写equals和hashCode方法才能实现唯一性。
HashMap
方法如下:
HashTable
方法如下:
TreeMap
方法如下:
collection的遍历
list
//实例化
ArrayList<String> arrayList = new ArrayList<String>();
//插入数据
arrayList.add("Qian");
arrayList.add("Fuxin");
//1、普通遍历
for (int i = 0; i < arrayList.size(); i++) {
String str = arrayList.get(i);
System.out.println(str);
}
//2、for-each 遍历
for (String str : arrayList) {
System.out.println(str);
}
//3、迭代器遍历
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
}
set
//实例化
HashSet<String> hashSet = new HashSet<String>();
//插入数据
hashSet.add("Qian");
hashSet.add("Fuxin");
//1、for-each遍历
for (String str : hashSet) {
System.out.println(str);
}
//2、迭代器遍历
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
}
queue
//实例化
Queue<Integer> queue = new LinkedBlockingQueue<Integer>();
/*
方法介绍
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
*/
//插入数据
for (int i = 0; i < 5; i++) {
queue.offer(i);
}
//1、for-each遍历
for (Integer x : queue) {
System.out.println(x);
}
//2、队列方式遍历,元素输出的同时会被清除
while (queue.peek() != null) {
System.out.println(queue.poll());
}
map
//实例化
HashMap<String, String> hashMap = new HashMap<String, String>();
//插入数据
hashMap.put("1", "2");
/*
1、map先转set然后再进行遍历
*/
//map的key转为set
Set<String> set = hashMap.keySet();
//for-each遍历
for (String key : set) {
String value = hashMap.get(key);
System.out.println("key:" + key + ",value:" + value);
}
/*
2、利用Entry进行遍历
*/
//转换为Entry集合
Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
//for-each遍历
for (Map.Entry<String, String> entry : entrySet) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println("key:" + key + ",value:" + value);
}
总结
最适合List的场景
需要保持元素的排列顺序且元素可以重复
小案例:用List记录公司的打卡记录
//实例化
ArrayList<String> arrayList = new ArrayList<String>();
//上班打卡
arrayList.add("小明");
arrayList.add("小红");
arrayList.add("小王");
//下班打卡
arrayList.add("小红");
arrayList.add("小王");
arrayList.add("小明");
//for-each 遍历
System.out.println("今天打卡的顺序如下");
for (String str : arrayList) {
System.out.println(str);
}
/*
输出:
今天打卡的顺序如下
小明
小红
小王
小红
小王
小明
*/
最适合Set的场景
需要元素不能重复,不在乎元素的排列顺序
小案例:计算一段话中有多少个不重复的字
//实例化
HashSet<String> hashSet = new HashSet<String>();
//经典台词
String word = "师爷:六个人,还当着人家男人的面,还开着灯。我都关着灯…太不要脸了,太不要脸了,呸!或者你花点钱,花不了多少钱,姑娘有的是…";
//字符串转数组
String words[] = word.split("");
//循环插入数据到set
for (int i = 0; i < words.length; i++) {
hashSet.add(words[i]);
}
System.out.println("该句话共有" + hashSet.size() + "个不重复的字符,详细字符如下。");
//for-each遍历数据
for (String str : hashSet) {
System.out.println(str);
}
/*
输出:一句话共有40个不重复的字符,详细字符如下。
着
开
要
!
。
的
者
了
师
有
,
不
我
少
姑
当
或
还
娘
:
多
你
面
…
个
太
六
灯
是
花
钱
关
家
爷
男
脸
呸
点
人
都
*/
最适合Map的场景
需要一对一的关系
小案例:计算一段话中每个字符出现的次数,并按照次数降序排列
//实例化 key存储字符,value存储字符出现的次数
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
//经典台词
String word = "师爷:六个人,还当着人家男人的面,还开着灯。我都关着灯…太不要脸了,太不要脸了,呸!或者你花点钱,花不了多少钱,姑娘有的是…";
//字符串转数组
String words[] = word.split("");
//循环插入数据到Map
for (int i = 0; i < words.length; i++) {
if (hashMap.containsKey(words[i])) {
//出现重复的字符,把出现的次数+1
hashMap.put(words[i], hashMap.get(words[i]) + 1);
} else {
//插入数据
hashMap.put(words[i], 1);
}
}
// map转list 方便按照value排序
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(hashMap.entrySet());
// 排序
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
// 降序
return o2.getValue() - o1.getValue();
}
});
//for-each遍历
for (Map.Entry<String, Integer> entry : list) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + ":出现" + value + "次");
}
/*
输出:
,:出现6次
着:出现3次
了:出现3次
不:出现3次
人:出现3次
要:出现2次
的:出现2次
还:出现2次
…:出现2次
太:出现2次
灯:出现2次
花:出现2次
钱:出现2次
脸:出现2次
开:出现1次
!:出现1次
。:出现1次
者:出现1次
师:出现1次
有:出现1次
我:出现1次
少:出现1次
姑:出现1次
当:出现1次
或:出现1次
娘:出现1次
::出现1次
多:出现1次
你:出现1次
面:出现1次
个:出现1次
六:出现1次
是:出现1次
关:出现1次
家:出现1次
爷:出现1次
男:出现1次
呸:出现1次
点:出现1次
都:出现1次
*/