集合
应用场景
- 无法预测存储数据的数量
- 同时存储具有一对一关系的数据
- 需要进行数据的增删
- 数据重复问题
体系结构
Conlection
Collection 接口有 3 种子类型集合: List
、Set
和 Queue
,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、ArrayBlockingQueue等。
List
- List是元素
有序并且可以重复
的集合,称为序列 - List可以精确的控制每个元素的插入位置,或删除某个位置的元素
- List的两个主要实现类是ArrayList和LinkedList
ArrayList
- 底层是由数组实现的
- 动态增长,以满足应用程序的需求
- 在列表尾部插入或删除数据非常有效
- 更适合查找和更新元素
- ArrayList的元素可以为
null
示例:
创建一个列表,向里面装入元素,并输出元素个数。
List list = new ArrayList();
// add()向列表中添加元素
list.add("Java");
list.add("C");
list.add("C ++");
list.add("Go");
list.add("C#");
list.add("JavaScript");
// size()输出元素个数
System.out.println("列表中元素个数:" + list.size());
示例:
遍历列表中元素。
for(int i = 0; i < list.size(); i ++) {
// 通过ger(i)下标获取列表中对应位置的元素
System.out.println(list.get(i));
}
示例:
移除列表中的元素。
list.remove(3); // 可以通过下标移除
list.remove("Go"); // 也可以根据值移除
案例:
公告管理
需求:
- 公告的添加和显示
- 在指定位置插入公告
- 删除公告
- 修改公告
// 创建Notice对象
Notice notice1 =new Notice(1, "第一条", "作者1", new Date());
Notice notice2 =new Notice(2, "第二条", "作者2", new Date());
Notice notice3 =new Notice(3, "第三条", "作者3", new Date());
Notice notice4 =new Notice(4, "第四条标题", "作者4", new Date());
// 添加公告列表
ArrayList noticeList = new ArrayList();
noticeList.add(notice1);
noticeList.add(notice2);
noticeList.add(notice3);
noticeList.add(notice4);
// 在第一条后面添加一条新公告
Notice notice5 = new Notice(5, "添加的信息", "管理员", new Date());
noticeList.add(1, notice5);
// 删除第三条公告
noticeList.remove(4);
// 將第一條公告title修改为 :最先发布的消息
notice1.setTitle("最先发布的消息");
noticeList.set(0, notice1); // 此处没有创建新的对象,可以不使用set方法,如果需要用新的对象来设置,则可以使用set方法修改
// 显示公告
System.out.println("公告内容为:");
for(int i = 0; i < noticeList.size(); i ++) {
System.out.println(i + 1 + ": " +((Notice) noticeList.get(i)).getTitle());
}
注意
- ArrayList实现了List接口,LinkedList实现了List接口,同时实现了Queue接口
- ArrayList的底层是数组,因此在内存中是连续存储的,
查询速度快,但增加和删除速度慢
- LinkedList底层是基于双向链表,
增加和删除速度快,而查询速度慢
Set
Set是元素无序并且不可以重复的集合,被称为集。
HashSet
- HashSet是Set的一个重要实现类,被称为哈希集
- HashSet中的元素无需并且
不可以重复
- HashSet中
只允许一个null元素
- 具有良好的存取和查找性能
hashCode与equals方法的作用
hashCode()
方法用于返回对象的hash code值,equals()
方法用于 判断其他对象与对象是否相等
为何要用这两种方法?
- 首先,
HashSet
里是不允许重复添加元素的,当调用add()
方法向HashSet
添加元素时,是通过hashCode()
和equals()
方法- 在添加数据时,会调用
hashCode()
方法,得到hash code值,通过这个值找到数据存储的位置,可以理解为存储的区域,在这个区域存储的hash code值是相等的- 如果这个区域已经有数据了,就继续调用
equals()
方法判断数据是否相等,如果相等则说明数据重复了,则不嫩个添加,如果不相等,就可以找到一个位置存储。- 如果两个对象相等,那么他们的
hashCode()
值一定相等。反之,如果两个对象的hashCode()
相等,那么他们不一定相等,还需要使用equals()
方法比较判断。
注意:
- 这些都是基于
哈希算法
完成的,它使得添加数据的效率得到了提升,假如Set集合中已经有100个元素,如果此时没有使用哈希算法,就需要调用equals()
方法将第101个元素与前100个元素依次进行比较,如果元素更多,比较耗费时间就越长。- 如果不重写
hashCode()
方法,默认每个对象的hashCode()
值都不一样,所以该类的每个对象都不会相等
Iterator(迭代器)
- Iterator接口可以以统一的方式对集合元素进行遍历
- hasNext()方法检测集合中是否还有下一个元素
- next()方法返回集合中的下一个元素
案例:
用HashSet存储多个表示颜色的英文单词,并输出。
// 创建set集合对象
Set set = new HashSet();
// 向集合中添加元素
set.add("blue");
set.add("red");
set.add("black");
set.add("green");
set.add("yellow");
// 创建迭代器,是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合
Iterator it = set.iterator();
// 遍历迭代器输出元素
while(it.hasNext()) { // 通过hasNext()判断如果迭代具有更多的元素,则返回true
System.out.println(it.next()); // 返回迭代中的下一个元素。
}
set.add("green"); // 重复元素添加不会添加成功,也不会报错
案例:
宠物猫信息管理
需求
- 添加和显示宠物猫的信息
- 查找某只宠物猫的信息并输出
- 修改宠物猫的信息
- 删除宠物猫的信息
Cat类
package com.listutil.com;
public class Cat {
private String name; // 姓名
private int month; // 年龄
private String species; // 品种
// 构造方法
public Cat(String name, int month, String species) {
this.setName(name);
this.setMonth(month);
this.setSpecies(species);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
@Override
public String toString() {
return "[姓名=" + name + ", 年龄=" + month + ", 品种=" + species + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + month;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((species == null) ? 0 : species.hashCode());
return result;
}
/**
@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
// 反射,判断是否属于Cat类
if(obj.getClass() == Cat.class) {
Cat cat = (Cat)obj;
return cat.getName().equals(name) && (cat.getMonth() == month) && cat.getSpecies().equals(species);
}
return false;
}
}
测试类
public static void main(String[] args) {
Cat huahua = new Cat("花花", 2, "波斯猫");
Cat fanfan = new Cat("凡凡", 1, "英短");
Cat huahua1 = new Cat("花花2代", 1, "波斯猫");
// 将宠物猫对象放入HashSet中
Set<Cat> set = new HashSet<Cat>();
set.add(huahua);
set.add(fanfan);
set.add(huahua1);
// 查找宠物猫信息
// 1. 通过对象名查找 huahua
if(set.contains(huahua)) {
System.out.println("通过对象名查找\"huahua\"找到了:");
System.out.println(huahua);
}else {
System.out.println("没找到\"huahua\"");
}
// 2. 通过集合中的名字信息查找 “花花”
Iterator<Cat> it = set.iterator();
boolean flag = false;
Cat c = null;
while(it.hasNext()) {
c = it.next(); // 引入泛型去掉了强制类型转换
if(c.getName().equals("花花")) {
flag = true;
break;
}
}
if(flag) {
System.out.println("通过集合查找姓名\"花花\"找到了:");
System.out.println(c);
}
// 删除花花2代信息
for(Cat cat : set) {
if("花花2代".equals(cat.getName())) {
set.remove(cat);break;
}
}
// 删除年龄小于2的
Set<Cat> set1 = new HashSet<Cat>();
for(Cat cat : set1) {
if(cat.getMonth() < 2) {
set1.add(cat);
}
}
set.removeAll(set1);
// 遍历输出
for(Cat cat : set) {
System.out.println(cat);
}
}
注意
- Set接口的主要实现类有
HashSet
和TreeSet
- HashSet是基于哈希表实现的,数组是无序的,
HashSet元素可以是null,但只能有一个
- TreeSet是基于二叉树实现的,
可以实现数据的自动排序,确保集合元素处于排序状态,不允许放入空值
- HashSet的性能优于TreeSet,一般情况建议使用HashSet,如果需要使用排序功能建议使用TreeSet
Map
- Map中的数据是以键值对(key-value)的形式存储的
- key-value以Entry类型的对象实例存在
- 可以通过key值快速地查找value
- 一个映射不能包含重复的键
HashMap
- 基于哈希表的Map接口的实现
- 允许使用null值和null键
- key值不允许重复
- HashMap中的Entry对象是无需排列的
案例:
完成一个类似字典的功能
需求:
- 将单词以及单词的注释存储到HashMap中
- 显示HashMap中的内容
- 查找某个单词的注释并显示
public static void main(String[] args) {
// 创建HashMap对象
HashMap<String, String> map = new HashMap<String, String>();
// 添加数据,map用put(key, value)方法添加key和value值
map.put("cat","猫");
map.put("dog","狗");
map.put("snake","蛇");
map.put("fire","火");
// 打印输出value的值
System.out.println("遍历输出value数据");
Iterator<String> it = map.values().iterator();
while(it.hasNext()) {
System.out.print(it.next() + " ");
}
// 打印输出key和value的值
System.out.println("通过entrySet方法得到key-value:");
// entrySet()方法返回一个映射的Set视图。
Set<Entry<String, String>> entrySet = map.entrySet();
for(Entry<String, String> entry : entrySet) {
System.out.print(entry.getKey() + "-");
System.out.println(entry.getValue());
}
System.out.println("********************");
// 通过单词查找注释并输出
System.out.println("请输入查找的单词:");
Scanner sc = new Scanner(System.in);
String strSearch = sc.next();
// 1.取得keySet,,返回包含的键的Set视图。
Set<String> keySet = map.keySet();
// 2.遍历keySet
boolean flag = false;
HashMap<String, String> wordMap = new HashMap<String, String>();
String k = null;
for(String key : keySet) {
if(strSearch.equals(key)) {
flag = true;
k = key;
wordMap.put(key, map.get(key));
break;
}
}
if(flag)
System.out.println("找到:" + k + ":" + wordMap.get(k));
else
System.out.println("没找到");
}
案例2:
商品信息管理
- 使用HashMap对商品信息进行管理
- 其中key为商品编号,value为商品对象
- 对HashMap中的商品信息进行增、删、改、查操作
Goods
类
public class Goods {
private String id; // 商品编号
private String name; // 商品名称
private double price; // 商品价格
public Goods(String id, String name, double price) {
this.setId(id);
this.setName(name);
this.setPrice(price);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return " [商品编号=" + id + ", 商品名称=" + name + ", 商品价格=" + price + "]";
}
}
测试类
public class GoodsTest {
public static void main(String[] args) {
// 定义HashMap对象
HashMap<String, Goods> goodsMap = new HashMap<String, Goods>();
// 装入商品信息
// if(goodsMap.containsKey(key))判断key是否存在,保证key值的唯一
goodsMap.put("s0001", new Goods("s0001", "棒棒糖", 2.5));
goodsMap.put("s0002", new Goods("s0002", "巧克力", 5));
goodsMap.put("s0003", new Goods("s0003", "饼干", 7.5));
goodsMap.put("s0004", new Goods("s0004", "卫龙辣条", 4.5));
// 增加商品
Scanner sc = new Scanner(System.in);
System.out.println("请输入商品编号:");
String goodsId;
while(true) {
goodsId = sc.next();
// 查询map里是否包含key值
if(goodsMap.containsKey(goodsId)) {
System.out.println("该商品编号已经存在,请重新输入!");
continue;
}else
break;
}
System.out.println("请输入商品名称:");
String goodsName = sc.next();
System.out.println("请输入商品价格:");
double goodPrice = 0;
while(true) {
try {
goodPrice = sc.nextDouble();
break;
}catch(InputMismatchException e) {
System.out.println("商品价格格式输入不正确,请输入数值型数据");
sc.next(); // 错误数据放到这个next()里
continue;
}
}
goodsMap.put(goodsId, new Goods(goodsId, goodsName, goodPrice));
// 删除商品
System.out.println("请输入要删除的商品名称:");
String deleteName = sc.next();
// 通过keySet()返回此地图中包含的键的Set视图
for(String key : goodsMap.keySet()) {
// 比较元素的name是否符合要删除的商品名称
if(goodsMap.get(key).getName().contains(deleteName)) {
goodsMap.remove(key);
}
}
// 修改商品名称
System.out.println("请选择要修改的商品:");
String oldName = sc.next();
if(goodsMap.containsValue(oldName)) {
for(String key : goodsMap.keySet()) {
// 比较元素的name是否符合要删除的商品名称
if(goodsMap.get(key).getName().contains(oldName)) {
Goods oldGood = goodsMap.get(key);
System.out.println("请输入修改的商品名称:");
String newName = sc.next();
oldGood.setName(newName);
goodsMap.replace(key, oldGood);
}
}
}else {
System.out.println("没有找到该商品");
}
// 遍历商品信息
Iterator it1 = goodsMap.values().iterator();
while(it1.hasNext()) {
System.out.println(it1.next());
}
}
注意
- Map的主要实现类包括HashMap和TreeMap,
其中HashMap基于哈希表实现,TreeMap基于红黑树实现
- HashMap
适用在Map中插入、删除和定位元素
- TreeMap
适用于自然顺序或自定义顺序对键值进行遍历
- HashMap比TreeMap性能好,所以HashMap使用更多一些,如果需要好对数据进行排序可以使用TreeMap