java中的容器——集合
1.集合定义
数组可以保存多个对象,但在无法确定需要保存多少个对象时,数组将不再适用,因为数组的长度不可变。
例如:
要保存一个学校的学生信息,由于不停有新生来报道,同时也有学员毕业离开学校,这时学生的数目无法固定,并且随时可能变动。
为了保存这些数目不确定的对象,Java中提供了集合,集合可以存储任意类型的对象,并且长度可变。
定义: | Java中的集合就像一个容器,专门用来存储Java对象。 |
---|---|
说明: | 集合对象可以是任意的数据类型,并且长度可变。 |
注意 : | 这些集合类都位于java.util包中,在使用时一定要注意导包的问题 |
2.集合分类
单列集合(Collection)和双列集合(Map)
集合的体系结构:
注意:虚线框里填写的都是接口类型,实线框里填写的都是具体的实现类
(1)单列集合Collection
1)单列集合根接口,用于存储一系列符合某种规则的元素 |
---|
2)Collection集合有两个重要的子接口,分别是List和Set |
3)List集合的特点是元素有序、可重复。该接口的主要实现类有ArrayList和LinkedList |
4)Set集合的特点是元素无序并且不可重复。该接口的主要实现类有HashSet和TreeSet。 |
(2)双列集合Map
1)双列集合根接口,用于存储具有键(Key)、值(Value)映射关系的元素 |
---|
2)Map集合中每个元素都包含一对键值,并且Key唯一,在使用Map集合时通过指定的Key找到对应的Value |
3)Map接口的主要实现类有HashMap和TreeMap。 |
3 Collection接口
方法声明 | 功能描述 |
---|---|
boolean add(Object o) | 向集合中添加一个元素 |
boolean addAll(Collection c) | 将指定集合c中的所有元素添加到该集合中 |
void clear() | 删除该集合中的所有元素 |
boolean remove(Object o) | 删除该集合中指定的元素 |
boolean removeAll(Collection c) | 删除该集合中包含指定集合c中的所有元素 |
boolean isEmpty() | 判断该集合是否为空 |
boolean contains(Object o) | 判断该集合中是否包含某个元素 |
boolean containsAll(Collection c) | 判断该集合中是否包含指定集合c中的所有元素 |
Iterator iterator() | 返回在该集合的元素上进行迭代的迭代器(Iterator),用于遍历该集合所有元素 |
int size() | 获取该集合元素个数 |
Stream stream() | 将集合源转换为有序元素的流对象(JDK 8新方法) |
(1)添加和删除数据
Collection c = new ArrayList();
c.add(1);//添加数据
c.add(3.14);
c.add("hello world");
System.out.println(c);
c.remove("hello world");//删除数据
System.out.println(c);
c.clear();//清空集合
System.out.println(c);
结果:
(2)判断
Collection c = new ArrayList();
System.out.println("集合是空的吗?"+c.isEmpty());
c.add("小明");
c.add("小红");
c.add("小白");
System.out.println("集合是空的吗?"+c.isEmpty());
System.out.println("集合中有小白吗?"+c.contains("小白"));
结果:
(3)获取
Collection c = new ArrayList();
c.add("小明");
c.add("小红");
c.add("小白");
System.out.println("集合的长度是?"+c.size());
结果:
(4)迭代器
/*
Iterator:迭代器
boolean hasNext();判断是否有下一个元素,有,返回true,否则返回false
E next();获取下个元素
*/
Collection c = new ArrayList();
c.add("小明");
c.add("小红");
c.add("小白");
//获取迭代器
Iterator iterator = c.iterator();
//使用迭代器遍历集合
while(iterator.hasNext()){//判断集合中有没有元素
//如果有,获取数据
Object obj = iterator.next();
System.out.println(obj);
}
结果:
5 ArrayList集合
说明: | ArrayList是List接口的一个实现类,它是程序中最常见的一种集合 ArrayList内部的数据存储结构是数组形式 |
---|---|
特点: | 由于ArrayList的存储结构,在增加或删除指定位置的元素时,会创建新的数组,效率比较低,因此不适合做大量的增删操作 这种数组结构允许程序通过索引的方式来访问元素,使用ArrayList集合在遍历和查找元素时显得非常高效 |
ArrayList list = new ArrayList();
list.add("stu1");
list.add("stu2");
System.out.println("集合的长度:" + list.size());
System.out.println("第2个元素是:" + list.get(1));
底层实现:
6 LinkList集合
说明: | LinkedList是List接口的另一个实现类。 LinkedList内部包含有两个Node类型的first和last属性的双向循环链表结构 |
---|---|
特点: | 由于LinkedList的存储结构, LinkedList集合对于元素的遍历和查找效率较低 LinkedList集合对于元素的增删操作表现出很高的效率 |
方法:
方法声明 | 功能描述 |
---|---|
void add(int index, E element) | 在此列表中指定的位置插入指定的元素。 |
void addFirst(Object o) | 将指定元素插入集合的开头 |
void addLast(Object o) | 将指定元素添加到集合的结尾 |
Object getFirst() | 返回集合的第一个元素 |
Object getLast() | 返回集合的最后一个元素 |
Object removeFirst() | 移除并返回集合的第一个元素 |
Object removeLast() | 移除并返回集合的最后一个元素 |
boolean offer(Object o) | 将指定元素添加到集合的结尾 |
boolean offerFirst(Object o) | 将指定元素添加到集合的开头 |
boolean offerLast(Object o) | 将指定元素添加到集合的结尾 |
Object peek() | 获取集合的第一个元素 |
---|---|
Object peekFirst() | 获取集合的第一个元素 |
Object peekLast() | 获取集合的最后一个元素 |
Object poll() | 移除并返回集合的第一个元素 |
Object pollFirst() | 移除并返回集合的第一个元素 |
Object pollLast() | 移除并返回集合的最后一个元素 |
void push(Object o) | 将指定元素添加到集合的开头 |
Object pop() | 移除并返回集合的第一个元素 |
代码:
LinkedList link = new LinkedList();
link.add("stu1");
link.add("stu2");
link.offer("offer"); // 向集合尾部追加元素
link.push("push"); // 向集合头部添加元素
Object object = link.peek(); //获取集合第一个元素
link.removeFirst(); // 删除集合第一个元素
link.pollLast(); // 删除集合最后一个元素
底层实现:
7 栈(stack)和队列(queue)
(1)栈stack
特点:
栈只能从表的一端存取数据,另一端是封闭的 |
---|
在栈中,无论是存数据还是取数据,都必须遵循"先进后出"的原则,即最先进栈的元素最后出栈 |
栈顶和栈底
栈顶 | 栈的开口端被称为栈顶,栈顶元素指的就是距离栈顶最近的元素 |
---|---|
栈底 | 栈的封口端被称为栈底,栈底元素指的是位于栈最底部的元素 |
操作:
进栈 | 向栈中添加元素,此过程被称为"进栈"(入栈或压栈); |
---|---|
出栈 | 从栈中提取出指定元素,此过程被称为"出栈"(或弹栈) |
总结:
栈是一种只能从表的一端存取数据且遵循 “先进后出” 原则的线性存储结构 |
---|
(2)队列queue
特点:
队列的两端都"开口",要求数据只能从一端进,从另一端出 |
---|
通常,称进数据的一端为 “队尾”,出数据的一端为 “队头” |
数据元素进队列的过程称为 “入队”,出队列的过程称为 “出队” |
总结:
数据从表的一端进,从另一端出,且遵循 “先进先出” 原则的线性存储结构就是队列 |
---|
8 遍历集合
(1)迭代器
List list = new ArrayList();
list.add("小明");
list.add("小黑");
list.add("小白");
//获取迭代器
Iterator iterator = list.iterator();
//使用迭代器遍历
while(iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
}
结果:
(2)增强for循环
/*
增强for循环格式:
for(数据类型 变量名 : 集合){
//变量名代表集合中的每个数据
}
执行原理:
依次遍历集合中的每个数据,并赋值给变量名,执行循环内容
*/
List list = new ArrayList();
list.add("小明");
list.add("小黑");
list.add("小白");
//使用增强for循环遍历集合
for(Object str : list){
System.out.println(str);
}
结果:
(3)索引值
List list = new ArrayList();
list.add("小明");
list.add("小黑");
list.add("小白");
//使用索引值遍历
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
结果:
9 Set接口
说明: | Set接口和List接口一样,同样继承自Collection接口。 |
---|---|
特点: | Set接口中的元素无序,并且都会以某种规则保证存入的元素不出现重复 |
10 HashSet集合
说明: | HashSet是Set接口的一个实现类,它所存储的元素不可重复,并且无序。 |
---|---|
特点: | 当向HashSet集合中添加一个元素时,首先会调用该元素的**hashCode()方法来确定元素的存储位置,然后再调用元素对象的equals()**方法来确保该位置没有重复元素。 |
(1)存储原理
总结:
当向集合中存入匀速时,为了保证HashSet正常工作,要求在存入对象时,重写hashCode()和equals()方法 |
---|
(2)示例
使用HashSet保存学生信息,根据xh判断是否是同一个学生
package com.ycy.test1;
import java.util.Objects;
public class Student {
public int xh;
public String name;
public Student() {
}
public Student(int xh, String name) {
this.xh = xh;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"xh=" + xh +
", name='" + name + '\'' +
'}';
}
//重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return xh == student.xh;
}
//重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(xh);
}
}
测试:
HashSet hashSet = new HashSet();
Student std1 = new Student(110, "小明");
Student std2 = new Student(110, "小明");
Student std3 = new Student(111, "小黑");
Student std4 = new Student(112, "小白");
hashSet.add(std1);
hashSet.add(std2);
hashSet.add(std3);
hashSet.add(std4);
System.out.println(hashSet);
结果:
分析:
11 TreeSet集合
说明: | TreeSet是Set接口的另一个实现类,它内部采用平衡二叉树来存储元素,来保证TreeSet集合中没有重复的元素,并且可以对元素进行排序。 |
---|---|
定义: | 二叉树就是每个节点最多有两个子节点的有序树,每个节点及其子节点组成的树称为子树,左侧的节点称为“左子树”,右侧的节点称为“右子树”,其中左子树上的元素小于它的根结点,而右子树上的元素大于它的根结点。 |
注意:
同一层的元素可分为1个根节点元素和2个子节点元素,左边的元素总是小于右边的元素。 |
---|
(1)方法:
方法声明 | 功能描述 |
---|---|
Object first() | 返回TreeSet集合的首个元素 |
Object last() | 返回TreeSet集合的最后一个元素 |
Object lower(Object o) | 返回TreeSet集合中小于给定元素的最大元素,如果没有返回null |
Object floor(Object o) | 返回TreeSet集合中小于或等于给定元素的最大元素,如果没有返回null |
Object higher(Object o) | 返回TreeSet集合中大于给定元素的最小元素,如果没有返回null |
Object ceiling(Object o) | 返回TreeSet集合中大于或等于给定元素的最小元素,如果没有返回null |
Object pollFirst() | 移除并返回集合的第一个元素 |
Object pollLast() | 移除并返回集合的最后一个元素 |
(2)存储原理
①TreeSet集合没有元素时,新增的第1个元素会在二叉树最顶层; |
---|
②接着新增元素时,首先会与根节点元素比较; |
③如果小于根节点元素就与左边的分支比较; |
④如果大于根节点元素就与右边的分支比较; |
⑤以此类推。 |
(2)示例
//向TreeSet中依次添加13、8、17、17、1、11、15、25元素
TreeSet treeSet = new TreeSet();
treeSet.add(13);
treeSet.add(8);
treeSet.add(17);
treeSet.add(17);
treeSet.add(1);
treeSet.add(11);
treeSet.add(15);
treeSet.add(25);
System.out.println(treeSet);
结果:
分析:
(3)元素排序
向TreeSet集合添加元素时,元素需要具备排序规则,也就是元素所在的类实现了Comparable接口,重写了compareTo()方法. |
---|
Java中大部分的类都实现了Comparable接口,并默认实现了接口中的CompareTo()方法,如Integer、Double和String等。 |
如果元素本身不具备排序规则,又想要添加到TreeSet集合中,可以自定义一个比较器
1)自定义比较器
自定义一个类,实现Comparator接口,重写compare()方法
public class 类名 implements Comparator {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
}
示例:
创建一个TreeSet,添加Student对象,按照xh排序
//自定义比较器
public class MyComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
if(o1 ==null || o2 == null || o1.getClass() != o2.getClass()) return 0;
Student std1 = (Student) o1;
Student std2 = (Student) o2;
return std1.xh-std2.xh;//根据xh比较
}
}
//创建TreeSet集合,并传入比较器
TreeSet treeSet = new TreeSet(new MyComparator());
Student std1 = new Student(115, "小明");
Student std2 = new Student(116, "小白");
Student std3 = new Student(112, "小黑");
Student std4 = new Student(115, "小明");
treeSet.add(std1);
treeSet.add(std2);
treeSet.add(std3);
treeSet.add(std4);
System.out.println(treeSet);
结果:
12 Map接口
说明: | Map接口是一种双列集合,它的每个元素都包含一个键对象Key和值对象Value,键和值对象之间存在一种对应关系,称为映射。 |
---|---|
特点: | Map中的映射关系是一对一的,一个键对象Key对应唯一一个值对象Value,其中键对象Key和值对象Value可以是任意数据类型,并且键对象Key不允许重复,这样在访问Map集合中的元素时,只要指定了Key,就能找到对应的Value。 |
(1)常用方法
方法声明 | 功能描述 |
---|---|
void put(Object key, Object value) | 向Map集合中添加指定键值映射的元素 |
int size() | 返回Map集合键值对映射的个数 |
Object get(Object key) | 返回指定键所映射的值,如果此映射不包含该键的映射关系,则返回null |
boolean containsKey(Object key) | 查看Map集合中是否存在指定的键对象key |
boolean containsValue(Object value) | 查看Map集合中是否存在指定的值对象value |
Object remove(Object key) | 删除并返回Map集合中指定键对象Key的键值映射元素 |
void clear() | 清空整个Map集合中的键值映射元素 |
Set keySet() | 以Set集合的形式返回Map集合中所有的键对象Key |
方法声明 | 功能描述 |
---|---|
Collection values() | 以Collection集合的形式返回Map集合中所有的值对象Value |
Set<Map.Entry<Key,Value>> entrySet() | 将Map集合转换为存储元素类型为Map的Set集合 |
Object getOrDefault(Object key, Object defaultValue) | 返回Map集合指定键所映射的值,如果不存在则返回默认值defaultValue(JDK 8新方法) |
(2)添加/修改/删除
//创建Map双列集合
Map map = new HashMap();
System.out.println(map);
//添加数据
map.put("name","小明");
map.put("age",11);
map.put("sex","男");
System.out.println(map);
//修改
map.put("age",12);
System.out.println(map);
//删除
map.remove("age");
System.out.println(map);
//清空
map.clear();
System.out.println(map);
结果:
(3)判断
//创建Map双列集合
Map map = new HashMap();
//添加数据
map.put("name","小明");
map.put("age",11);
map.put("sex","男");
System.out.println(map);
//判断
System.out.println("有age吗?"+map.containsKey("age"));
System.out.println("有小明吗?"+map.containsValue("小明"));
结果:
(4)获取
//创建Map双列集合
Map map = new HashMap();
//添加数据
map.put("name","小明");
map.put("age",11);
map.put("sex","男");
System.out.println(map);
//根据key获取对应的value
System.out.println("name="+map.get("name"));
//获取所有的key
Set set = map.keySet();
System.out.println("所有的keys="+map.keySet());
//获取所有的value
System.out.println("所有的values="+map.values());
结果:
(5)转换(可以用来遍历Map集合)
//创建Map双列集合
Map map = new HashMap();
//添加数据
map.put("name","小明");
map.put("age",11);
map.put("sex","男");
System.out.println(map);
//转换:将Map转成Set集合
Set set = map.entrySet();
System.out.println(set);
//遍历set集合
for(Object obj : set){
//set集合中存储的数据类型为: Map.Entry
Map.Entry entry = (Map.Entry) obj;
//获取key
Object key = entry.getKey();
//获取value
Object value = entry.getValue();
System.out.println(key+":"+value);
}
结果:
(6)遍历Map集合
方式一:转换,如(5)中代码所示
方式二:根据key遍历Map集合
//创建Map双列集合
Map map = new HashMap();
//添加数据
map.put("name","小明");
map.put("age",11);
map.put("sex","男");
//获取所有的key
Set keys = map.keySet();
//遍历所有的key
for(Object key : keys){
//根据key获取对应的value
Object value = map.get(key);
System.out.println(key+"="+value);
}
结果:
13 HashMap集合
说明: | HashMap集合是Map接口的一个实现类,它用于存储键值映射关系,该集合的键和值允许为空,但键不能重复,且集合中的元素是无序的。 |
---|---|
特点: | HashMap底层是由哈希表结构组成的,其实就是“数组+链表”的组合体,数组是HashMap的主体结构,链表则主要是为了解决哈希值冲突而存在的分支结构。正因为这样特殊的存储结构,HashMap集合对于元素的增、删、改、查操作表现出的效率都比较高。 |
代码:
//创建Map双列集合
Map map = new HashMap();
//添加数据
map.put("name","小明");
map.put("name","小明2");
map.put("age",11);
map.put("sex","男");
结果:
存储原理:
14 TreeMap集合
介绍: | TreeMap集合是Map接口的另一个实现类,在TreeMap内部是通过二叉树的原理来保证键的唯一性,这与TreeSet集合存储的原理一样,因此TreeMap中所有的键是按照某种顺序排列的。 |
---|---|
说明: | 为了实现TreeMap元素排序,可以参考TreeSet 集合排序方式,使用自然排序和定制排序。 |
代码:
//创建TreeMap集合
TreeMap map = new TreeMap();
//添加数据
map.put("name","小明");
map.put("name","小明2");
map.put("age",11);
map.put("sex","男");
System.out.println(map);
结果:
分析:
15 Properties集合
Properties主要用来存储字符串类型的键和值,在实际开发中,经常使用Properties集合类来存取应用的配置项 |
---|
例1:
Properties properties = new Properties();
//存储数据
properties.setProperty("name","小明");
properties.setProperty("sex","男");
//获取数据
System.out.println("name="+properties.getProperty("name"));
System.out.println("sex="+properties.getProperty("sex"));
//删除数据
properties.remove("name");
System.out.println(properties);
结果:
例2:
//创建db.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/school
username=root
password=root123
Properties properties = new Properties();
File file = new File("C:\\Users\\Administrator\\Desktop\\db.properties")
FileInputStream in = new FileInputStream(file);
properties.load(in);
System.out.println(properties);
System.out.println(properties.getProperty("username"));
结果:
16 Collections工具类
说明: | Java提供了一个工具类专门用来操作集合,这个类就是Collections,它位于java.util包中。 Collections类中提供了大量的静态方法用于对集合中元素进行排序、查找和修改等操作。 |
---|
常用方法:
方法声明 | 功能描述 |
---|---|
static boolean addAll(Collection<? super T> c, T… elements) | 将所有指定元素添加到指定集合c中 |
static void reverse(List list) | 反转指定List集合中元素的顺序 |
static void shuffle(List list) | 对List集合中的元素进行随机排序 |
static void sort(List list) | 根据元素的自然顺序对List集合中的元素进行排序 |
static void swap(List list,int i,int j) | 将指定List集合中角标i处元素和j处元素进行交换 |
方法声明 | 功能描述 |
---|---|
static int binarySearch(List list,Object key) | 使用二分法搜索指定对象在List集合中的索引,查找的List集合中的元素必须是有序的 |
static Object max(Collection col) | 根据元素的自然顺序,返回给定集合中最大的元素 |
static Object min(Collection col) | 根据元素的自然顺序,返回给定集合中最小的元素 |
static boolean replaceAll(List list,Object oldVal,Object newVal) | 用一个新值newVal替换List集合中所有的旧值oldVal |
例如:
ArrayList list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
System.out.println("list="+list);
//添加多个数据
Collections.addAll(list,"a","b","c");
System.out.println("添加多个数据后的list="+list);
//反转
Collections.reverse(list);
System.out.println("反转后的list="+list);
//随机排序
Collections.shuffle(list);
System.out.println("随机排序后的list="+list);
//交换元素
Collections.swap(list,0,list.size()-1);
System.out.println("交换元素后的list="+list);
//找最大值
System.out.println("max="+Collections.max(list));
//找最小值
System.out.println("min="+Collections.min(list));
//替换
Collections.replaceAll(list,"a","z");
System.out.println("替换后的list="+list);
结果: