JavaSE:集合框架
文章目录
前言
即使再小的帆也能远航
一、集合框架概述
- 集合的概念
- Collection接口
- List接口与实现类
- 泛型和工具类
- Set接口与实现类
- Map接口与实现类
二、集合概念
1.什么是集合
- 概念:对象的容器,定义了多个对象进行操作的常用方法。可实现数组的功能
- 和数组区别:
- 数组长度固定(不可变),集合长度不固定(可变)
- 数组可以存储基本类型和引用类型,集合只能存储引用类型
- 位置:java.util.*;
util包有两个顶层接口,一个collection,一个map
三、Collection接口(集合)
1. Collection体系集合
- Collection:该体系结构的根接口,代表一组对象,成为“集合”
- List接口的特点:有序、有下标、元素可重复
- Set接口的特点:无序、无下标、元素不能重复
ArrayList数组、LinkedList链表、Vector线程安全
2. Collection接口
- Collection是个父接口
- 特点:代表一组任意类型的对象,无序、无下标(所以不能用for循环直接调用,但可以用增强for循环foreach)、不能重复
- 方法
- boolean add(Object ibj) //添加一个对象
- boolean addAll(Collection c) //将一个集合中的所有对象添加到此集合中
- void clear() //清空此集合中的所有对象
- boolean contains(Object o) //检查此集合中是否含有o对象
- boolean equals(Object o) //比较此集合中是否与指定对象相等
- boolean isEmpty() //判断此集合是否为空
- boolean remove(Object o) //在此集合中移除o对象
- int size() //返回此集合中的元素个数
- Object[] toArray() //将此集合转换成数组
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
Collection的使用
1. 添加
2. 删除
3. 遍历
4. 判断
*/
public class Demo01 {
public static void main(String[] args) {
Collection collection = new ArrayList();
//1. 添加
collection.add("苹果");
collection.add("西瓜");
collection.add("榴莲");
System.out.println("元素个数:"+collection.size());
System.out.println(collection);
// //2. 删除
// collection.remove("榴莲");
// System.out.println(collection);
// //清空
// collection.clear();
// System.out.println(collection.size());
//3. 遍历
// 3.1 使用增强for eg.collection.for
for (Object o : collection) {
System.out.println(o);
}
System.out.println("========");
// 3.2 使用迭代器(专门用来遍历集合的一种方式)
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
Object o = iterator.next();
System.out.println(o);
//collection.remove(o); //ConcurrentModificationException并发修改异常,不能同时修改又删除
// iterator.remove();
// System.out.println(o);
// //删除功能最后执行
}
// System.out.println(collection);
System.out.println("========");
//4. 判断(包含)(判空)
System.out.println(collection.contains("西瓜"));
System.out.println(collection.isEmpty());
collection.clear();
System.out.println(collection.isEmpty());
}
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
Collection的使用:保存学生信息
*/
public class Demo02 {
public static void main(String[] args) {
Collection collection = new ArrayList();
Student s1 = new Student("张三",20);
Student s2 = new Student("李四",18);
Student s3 = new Student("王五",23);
//1. 添加
collection.add(s1);
collection.add(s2);
collection.add(s3);
System.out.println(collection.size());
System.out.println(collection);
//2. 删除
collection.remove(s1);
System.out.println(collection.size());
System.out.println(s1); //集合内该元素被删除(删的是指针),但是内存还在
//有无以上这句话3.1的结果会不一样 3.2不变
//3. 遍历
//3.1 增强for
for (Object o : collection) {
System.out.println(o);
}
System.out.println("========");
//3.2 迭代器
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
Student s = (Student)iterator.next();
System.out.println(s);
}
//4. 判断
System.out.println(collection.contains("张三"));
System.out.println(collection.contains("李四"));
System.out.println(collection.contains(s2));
System.out.println(collection.isEmpty());
}
}
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
//1. 判断俩个对象是否是同一个引用,如果是那肯定相等
if (this == o) return true;
//2. 判断o是否为空以及是否是同一个类型,传进来空或者不是同一个类型的那肯定不相等
if (o == null || getClass() != o.getClass()) return false;
//3. 判断对象是否相等
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
protected void finalize() throws Throwable {
//原方法体为空,为判断是否执行添加了输出语句
System.out.println(this.name+"对象被回收了");
}
}
1. List接口
- List集合、List子接口
- 特点:有序、有下标、元素可以重复
1. List使用
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* List子接口的使用
* 特点:1. 有序 有下标 2. 可以重复
*/
public class Demo01 {
public static void main(String[] args) {
List list = new ArrayList();
//1. 添加
list.add("苹果");
list.add("西瓜");
list.add("榴莲");
list.add("香蕉");
System.out.println("元素个数:"+list.size());
System.out.println(list);
//2. 删除
list.remove(0);
list.remove("西瓜");
System.out.println(list);
System.out.println("========");
//3. 遍历
//3.1 使用for遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("========");
//3.2 使用增强for
for (Object o : list) {
System.out.println(o);
}
//3.3 使用迭代器
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("========");
//3.4 列表迭代器
//和Iterator的区别:
//listIterator可以向前或向后遍历,添加、删除、修改元素
ListIterator lit = list.listIterator();
System.out.println("从前往后遍历:");
while(lit.hasNext()){
System.out.println(lit.nextIndex()+":"+lit.next());
}
System.out.println("========");
System.out.println("从后往前遍历:");
while(lit.hasPrevious()){
System.out.println(lit.previousIndex()+":"+lit.previous());
}
System.out.println("========");
//4. 判断
System.out.println(list.contains("苹果"));
System.out.println(list.isEmpty());
//5. 获取位置
System.out.println(list.indexOf("榴莲"));
}
}
import java.util.ArrayList;
import java.util.List;
public class Demo02 {
public static void main(String[] args) {
List list = new ArrayList();
//1. 添加 集合是不能保存基本类型的,所以会发生自动装箱
list.add(20);
list.add(30);
list.add(40);
System.out.println(list.size());
System.out.println(list);
//2. 删除
//删除为20的数据的三种操作:把基本类型转为引用类型
// list.remove("20");
// list.remove((Object)20);
// list.remove(new Integer(20));
//3. 补充方法sublist,返回子集合,含头不含尾(左闭右开区间)
List subList = list.subList(1, 2);
System.out.println(subList);
System.out.println(list.subList(1,3));
}
}
2. List实现类
- ArrayList实现类【重点】:
- 数组结构实现,查询快、增删慢
- JDK1.2版本后加入该类,运行效率快、线程不安全
- Vector:
- 数组结构实现,查询快、增删慢
- JDK1.0版本后加入该类,运行效率慢、线程安全(加锁)
- LinkedList:
- 链表结构实现,增删快、查询慢
1. ArrayList
1. ArrayList使用
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
/**
* ArrayList的使用:
* 存储结构:数组,查找遍历速度快,增删慢
*/
public class Demo03 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
//1.添加
Student s1 = new Student("张三",20);
Student s2 = new Student("李四",18);
Student s3 = new Student("王五",23);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
System.out.println("元素个数:"+arrayList.size());
System.out.println(arrayList.toString());
System.out.println("========");
//2. 删除
// arrayList.remove(s1);
// System.out.println("删掉后元素个数:"+arrayList.size());
// System.out.println(arrayList);
// arrayList.remove(new Student("李四",18));
// System.out.println(arrayList);
// System.out.println("========");
//3. 遍历
//3.1 迭代器
Iterator lt = arrayList.iterator();
while(lt.hasNext()){
Student s = (Student)lt.next();
System.out.println(s.toString());
}
System.out.println("========");
//3.2 ArrayList使用
ListIterator lit = arrayList.listIterator();
while (lit.hasNext()){
Student s = (Student)lit.next();
System.out.println(s);
}
System.out.println("========");
while (lit.hasPrevious()){
Student s = (Student)lit.previous();
System.out.println(s);
}
System.out.println("========");
//4. 判断
System.out.println(arrayList.contains(new Student("李四",18)));
System.out.println(arrayList.isEmpty());
//5. 查找
System.out.println(arrayList.indexOf(new Student("李四",18)));
}
}
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
//1. 判断俩个对象是否是同一个引用,如果是那肯定相等
if (this == o) return true;
//2. 判断o是否为空,传进来空的那肯定不相等
if (o == null) return false;
//3. 判断对象是否相等
if (o instanceof Student) {
Student student = (Student) o;
//4. 比较属性
if (this.name.equals(student.getName())&&this.age== student.getAge()) {
return true;
}
}
//5. 不满足条件 返回false
return false;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
protected void finalize() throws Throwable {
//原方法体为空,为判断是否执行添加了输出语句
System.out.println(this.name+"对象被回收了");
}
}
2. ArrayList源码分析
- 默认容量 DEFAULT_CAPACITY = 10
(刚创建没有向集合中添加元素时,容量为0,添加第一个元素后就扩容为10,容量不足时扩容,每次扩容大小是原来的1.5倍) - 存放元素的数组 elementData
- 实际元素个数 size
- 添加元素add()
2. Vector
1. Vector使用
import java.util.Enumeration;
import java.util.Vector;
/**
* 演示Vector集合的使用
* 存储结构:数组
*/
public class Demo04 {
public static void main(String[] args) {
Vector vector = new Vector();
//1.添加
vector.add("草莓");
vector.add("芒果");
vector.add("西瓜");
System.out.println(vector.size());
System.out.println(vector);
//2. 删除
// vector.remove(0);
// System.out.println(vector);
// vector.remove("西瓜");
// System.out.println(vector);
// vector.clear();
// System.out.println(vector.size());
//3. 遍历
//使用枚举器
Enumeration en = vector.elements();
while (en.hasMoreElements()){
String o = (String)en.nextElement();
System.out.println(o);
}
System.out.println("========");
//4. 判断
System.out.println(vector.contains("西瓜"));
System.out.println(vector.isEmpty());
System.out.println("========");
//5. 其他方法
//firstElement、lastElement、elementAt
System.out.println(vector.firstElement());
System.out.println(vector.lastElement());
System.out.println(vector.elementAt(1));
}
}
3. LinkedList
1. LinkedList使用
- 链表结构实现,增删快,查询慢
import java.util.Iterator;
import java.util.LinkedList;
/**
* LinkedList的使用
* 存储结构:双向链表
*/
public class Demo05 {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
Student s1 = new Student("张三",20);
Student s2 = new Student("李四",18);
Student s3 = new Student("王五",23);
//1. 添加
linkedList.add(s1);
linkedList.add(s2);
linkedList.add(s3);
System.out.println(linkedList.size());
System.out.println(linkedList);
//2. 删除
linkedList.remove(new Student("李四",18));
System.out.println(linkedList);
// linkedList.clear();
//3. 遍历
//3.1 for遍历
System.out.println("========");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
//3.2 增强for
System.out.println("========");
for (Object o : linkedList) {
Student s = (Student)o;
System.out.println(o);
}
//3.3 使用迭代器
System.out.println("========");
Iterator it = linkedList.iterator();
while(it.hasNext()){
Student s = (Student)it.next();
System.out.println(s);
}
//4. 判断
System.out.println("========");
System.out.println(linkedList.contains(s1));
System.out.println(linkedList.isEmpty());
//5. 获取
System.out.println(linkedList.indexOf(s1));
System.out.println(linkedList.indexOf(s2));
}
}
2. LinkedList源码分析
(先学数据结构再看这个)
- size 集合的大小
- Node first 链表的头结点
- Node last 链表的尾结点
4. ArrayList和LinkedList的区别
- 不同结构实现方式
- A:必须开辟连续空间,查询快,增删慢
- L:无需开辟连续空间,查询慢,增删快
2. 泛型
1. 泛型概述
- JDK1.5新特性,其本质是参数化类型,把类型作为参数传递
- 常见形式有:泛型类、泛型接口、泛型方法
- 语法<T,…> T称为类型占位符,表示一种引用类型(T或E、K、V都可)
- 好处:
- 提高代码的重用性
- 防止类型转换异常,提高代码的安全性
2. 泛型类
/**
* 泛型类
* 语法:类名<T> 即写在类名后面
* T是类型占位符,表示一种引用类型,如果编写多个可以使用逗号隔开
*/
public class Demo01<T> {
//使用泛型T
//1. 创建变量
T t;
//2. 泛型作为方法的参数
public void show(T t){
//T t1 = new T(); //因为类型不确定,所以是否能够被创建也不确定
System.out.println(t);
}
//3. 泛型作为方法的返回值
public T getT(){
return t;
}
}
public class Test01 {
public static void main(String[] args) {
//使用泛型类来创建对象
// 注意:
// 1. 泛型只能使用引用类型
// 2. 不同泛型类型对象之间不能相互赋值
Demo01<String> fanxing = new Demo01<String>();
fanxing.t = "hello";
fanxing.show("大家好!");
String s = fanxing.getT();
//fanxing.getT();
Demo01<Integer> fanxing2 = new Demo01<Integer>();
fanxing2.t = 100;
fanxing2.show(200);
Integer i = fanxing2.getT();
//fanxing2.getT();
}
}
3. 泛型接口
/**
* 泛型接口
* 语法:接口名<T>
* 注意:不能泛型静态常量(static类同时加载,但此时泛型类型还不确定,无法被创建)
*/
public interface Demo02<T> {
String name = "张三";
T server(T t);
}
public class Demo02Impl implements Demo02<String>{
@Override
public String server(String s) {
System.out.println(s);
return s;
}
}
public class Demo02Impl2<T> implements Demo02<T>{
@Override
public T server(T t) {
System.out.println(t);
return t;
}
}
//泛型接口
public class Test02 {
public static void main(String[] args) {
//一种是用之前确定类型,一种是用时确定类型
//1. 用前确定
Demo02Impl impl = new Demo02Impl();
impl.server("Hello World!");
//2. 用时确定
Demo02Impl2<Integer> impl2 = new Demo02Impl2<>();
impl2.server(520);
}
}
4. 泛型方法
/**
* 泛型方法 //MyGenericMethod
* 语法:<T> 返回值类型
*/
public class Demo03 {
//泛型方法 范围:方法内
public <T> void show(T t){
System.out.println("泛型方法:"+t);
}
//<T>声明泛型方法 T返回值类型 (T t)作为泛型参数
public <T> T go(T t){
System.out.println("泛型方法:"+t);
return t;
}
}
public class Test03 {
public static void main(String[] args) {
Demo03 demo03 = new Demo03();
//泛型方法可传递任何类型
demo03.show("Hello World"); //T-->String
demo03.show(520); //T-->Integer
//上面的返回值类型为空,下面的返回值类型为泛型T
Integer go = demo03.go(1314);
//可以在调用方法前定义类型进行限制传递的类型
demo03.<Double>go((double) 1314);
}
}
5. 泛型好处
- 好处:
- 提高代码的重用性
- 防止类型转换异常,提高代码的安全性
6. 泛型集合
- 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致
- 特点:
- 编译时即可检查,而非运行时抛出异常
- 访问时,不必类型转换(拆箱)
- 不同泛型之间引用不能相互赋值,泛型不存在多态
import java.util.LinkedList;
public class Demo04 {
public static void main(String[] args) {
//LinkedList<E> 未指定类型时,默认为Object类型对象
LinkedList linkedList = new LinkedList();
}
}
import java.util.ArrayList;
public class Demo05 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("xxx");
arrayList.add("yyy");
arrayList.add(10);
arrayList.add(20);
System.out.println(arrayList);
for (Object o : arrayList) {
String str = (String)o;
System.out.println(str);
//Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
}
}
}
import java.util.ArrayList;
import java.util.Iterator;
public class Demo05_2 {
public static void main(String[] args) {
//限制泛型类型 为String
ArrayList<String> arrayList = new ArrayList<String >();
arrayList.add("xxx");
arrayList.add("yyy");
//限制泛型类型后,不能添加其他类型
// arrayList.add(10);
// arrayList.add(20);
System.out.println(arrayList);
for (Object o : arrayList) {
System.out.println(o);
}
System.out.println("========");
ArrayList<Student> arrayList1 = new ArrayList<Student>();
Student s1 = new Student("张三",20);
Student s2 = new Student("李四",18);
Student s3 = new Student("王五",23);
arrayList1.add(s1);
arrayList1.add(s2);
arrayList1.add(s3);
Iterator<Student> it = arrayList1.iterator();
while(it.hasNext()){
Student s = it.next();
System.out.println(s);
}
//不同泛型创建出来的对象不能相互赋值
//arrayList1=arrayList;
}
}
3. Set集合
1. Set集合概述
- Set子接口
- 特点:无序、无下标、元素不可重复
- 全部继承自Collection中的方法
2. Set接口使用
- Set实现类:
- HashSet【重点】:
- 基于HashCode实现元素不重复
- 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入
- TreeSet:
- 基于排列顺序实现元素不重复
- HashSet【重点】:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* 测试Set接口的使用
* 特点:(1)无序、没有下标 (2)不能重复
*/
public class Demo01 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
//1. 添加
set.add("小米");
set.add("苹果");
set.add("华为");
set.add("小米");
System.out.println("数据个数:"+set.size());
System.out.println(set); //[苹果, 华为, 小米]
//2. 删除
set.remove("小米");
System.out.println(set);
//3. 遍历
//3.1 使用增强for
System.out.println("========");
for (String s : set) {
System.out.println(s);
}
//3.2 使用迭代器
System.out.println("========");
Iterator<String > it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//4. 判断
System.out.println("========");
System.out.println(set.contains("华为"));
System.out.println(set.isEmpty());
}
}
1. HashSet
1. HashSet使用
- HashSet【重点】:
- 基于HashCode实现元素不重复
- 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入
import java.util.HashSet;
import java.util.Iterator;
/**
* HashSet集合的使用
* 存储结构:哈希表(数组+链表+红黑树(JDK1.8))
* 举例子:排队买票:排队的队伍是单向链表,购票的窗口是数组
*/
public class Demo02 {
public static void main(String[] args) {
HashSet<String > hashSet = new HashSet<>();
//1. 添加
hashSet.add("刘德华");
hashSet.add("梁朝伟");
hashSet.add("林志玲");
hashSet.add("周润发");
hashSet.add("刘德华");
System.out.println("元素个数:"+hashSet.size());
System.out.println(hashSet);
//2. 删除
hashSet.remove("刘德华");
System.out.println("删除之后:"+hashSet.size());
//3. 遍历
//3.1增强for
System.out.println("========");
for (String s : hashSet) {
System.out.println(s);
}
//3.2 使用迭代器
System.out.println("========");
Iterator<String > it = hashSet.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//4. 判断
System.out.println(hashSet.contains("郭富城"));
System.out.println(hashSet.isEmpty());
}
}
import java.util.HashSet;
/**
* HashSet集合的使用
* 存储结构:哈希表(数组+链表+红黑树(JDK1.8))
* 举例子:排队买票:排队的队伍是单向链表,购票的窗口是数组
*/
public class Demo03 {
public static void main(String[] args) {
HashSet<Person> person = new HashSet<>();
//1. 添加
Person p1 = new Person("刘德华",20);
Person p2 = new Person("林志玲",22);
Person p3 = new Person("梁朝伟",25);
person.add(p1);
person.add(p2);
person.add(p3);
person.add(p3);
person.add(new Person("梁朝伟",25));
System.out.println("元素个数:"+person.size());
System.out.println(person.toString()); //不写默认也是调用toString方法
}
}
2. HashSet存储方式
import java.util.HashSet;
import java.util.Iterator;
/**
* HashSet集合的使用
* 存储结构:哈希表(数组+链表+红黑树(JDK1.8))
* 存储过程(重复依据):
* (1)根据hashcode计算保存的位置,如果位置为空,则直接保存,如果不为空执行第二步
* (2)再执行equals方法,如果equals方法为true,则认为重复,否则,完成链表
* 举例子:排队买票:排队的队伍是单向链表,购票的窗口是数组
*/
public class Demo03 {
public static void main(String[] args) {
HashSet<Person> persons = new HashSet<>();
//1. 添加
Person p1 = new Person("刘德华",20);
Person p2 = new Person("林志玲",22);
Person p3 = new Person("梁朝伟",25);
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p3); //失败
persons.add(new Person("梁朝伟",25)); //失败!
System.out.println("元素个数:"+persons.size());
System.out.println(persons.toString()); //不写默认也是调用toString方法
//2. 删除
//persons.remove(p1);
persons.remove(new Person("林志玲",22));
System.out.println("删除之后:"+persons.size());
//3. 遍历
//3.1 使用增强for
System.out.println("========");
for (Person person : persons) {
System.out.println(person);
}
//3.2 迭代器
//set无序 没有列表迭代器
System.out.println("========");
Iterator<Person> it = persons.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println("========");
//4. 判断
System.out.println(persons.contains(p1));
System.out.println(persons.contains(new Person("刘德华",20)));
}
}
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
//先重写hashcode保证相同属性对象生成相同哈希码值,不然俩个属性相同的对象依然可能会分到不同的数组
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
}
3. HashSet补充
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
//(1)31是一个质数,减少散列冲突(2)31提高执行效率 31*i=(31<<5)-i
return result;
}
2. TreeSet
1. TreeSet概述
- 红黑树(是二叉树的一种)
- 基于排列顺序实现元素不重复
- 实现了SortedSet接口,对集合元素自动排序
- 元素对象的类型必须实现Comparatable接口,指定排序规则
- 通过CompareTo方法确定是否为重复元素
2. TreeSet使用
import java.util.Iterator;
import java.util.TreeSet;
/**
* TreeSet的使用
* 存储结构:红黑树
*/
public class Demo05 {
public static void main(String[] args) {
TreeSet<String > treeSet = new TreeSet<>();
//1. 添加
treeSet.add("xyz");
treeSet.add("abc");
treeSet.add("hello");
treeSet.add("xyz");
System.out.println("元素个数:"+treeSet.size());
System.out.println(treeSet.toString()); //[abc, hello, xyz]
//2. 删除
treeSet.remove("xyz");
System.out.println("删除之后:"+treeSet.size());
//3. 遍历
//3.1 使用增强for
System.out.println("========");
for (String s : treeSet) {
System.out.println(s);
}
//3.2 使用迭代器
System.out.println("========");
Iterator<String> it = treeSet.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//4. 判断
System.out.println(treeSet.contains("abc"));
}
}
import java.util.Iterator;
import java.util.TreeSet;
/**
* 使用TreeSet保存数据
* 存储结构:红黑树(必须要对输入进集合的元素作比较)
* 要求:元素必须要实现Comparable接口,ComparableTo()方法返回值为0,认为是重复元素
*/
public class Demo06 {
public static void main(String[] args) {
TreeSet<Person> persons = new TreeSet<>();
//1. 添加
Person p1 = new Person("xyz",20);
//java.lang.ClassCastException: Person cannot be cast to java.lang.Comparable
//所以要求元素必须实现Comparable接口才可进行比较(红黑树)存储
Person p2 = new Person("hello",22);
Person p3 = new Person("abc",25);
Person p4 = new Person("abc",20);
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p3);
persons.add(p4);
System.out.println("元素个数:"+persons.size());
System.out.println(persons); //小前大后
//2. 删除
persons.remove(new Person("abc",20));
System.out.println("删除之后:"+persons.size());
System.out.println(persons);
//3. 遍历
//3.1 使用增强fot
System.out.println("========");
for (Person person : persons) {
System.out.println(person);
}
System.out.println("========");
//3.2 使用迭代器
Iterator<Person> it = persons.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("========");
//4. 判断
System.out.println(persons.contains(new Person("abc",20)));
}
}
import java.util.Objects;
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
//先重写hashcode保证相同属性对象生成相同哈希码值,不然俩个属性相同的对象依然可能会分到不同的数组
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int compareTo(Person o) {
int n1 = this.getName().compareTo(o.getName());
int n2 = this.age-o.getAge();
return n1==0?n2:n1;
//先按姓名比,再按年龄比
//如果没有CompareTo方法,红黑树无法进行比较存储,则会报错
}
}
3. Comparator接口
import java.util.Comparator;
import java.util.TreeSet;
/**
* TreeSet集合的使用
* Comparator:实现定制比较(比较器)
* Compatable:可比较的
*/
public class Demo07 {
public static void main(String[] args) {
TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() { //匿名内部类
@Override
public int compare(Person o1, Person o2) {
int n1 = o1.getAge()-o2.getAge();
int n2 = o1.getName().compareTo(o2.getName());
return n1==0?n2:n1;
}
});
Person p1 = new Person("xyz",20);
Person p2 = new Person("hello",22);
Person p3 = new Person("abc",25);
Person p4 = new Person("lisi",20);
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
System.out.println(persons);
}
}
4. TreeSet案例
import java.util.Comparator;
import java.util.TreeSet;
/**
* 要求:使用TreeSet集合实现字符串按照长度进行排序
* helloworld zhangsan lisi wangwu beijing xian nanjing
*/
public class Demo08 {
public static void main(String[] args) {
TreeSet<String > treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int n1 = o1.length()-o2.length();
int n2= o1.compareTo(o2);
return n1==0?n2:n1;
}
});
//添加元素
treeSet.add("helloworld");
treeSet.add("zhangsan");
treeSet.add("lisi");
treeSet.add("wangwu");
treeSet.add("beijing");
treeSet.add("xian");
treeSet.add("nanjing");
System.out.println(treeSet);
}
}
四、Map体系集合
1.Map集合概述
Map父接口
- 特点:
- 用于存储任意键值对(key-value)
- 键:无序、无下标、不允许重复(唯一)
- 值:无序、无下标、允许重复
- 方法:
- V put (K key , V value) //将对象存入到集合中,关联键值。key重复则覆盖原值
- Object get (Object key) //根据键获取对应的值
- Set //返回所有Key
- Collection values() //返回包含所有值的Collection集合
- Set<Map.Entry<K,V>> //键值匹配的Set集合
2. Map接口使用
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Map接口的使用
* 特点:(1)存储键值对(2)键不能重复,值可以重复(3)无序
*/
public class Demo01 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
//1. 添加
map.put("cn","中国");
map.put("uk","英国");
map.put("usa","美国");
map.put("cn","zhongguo"); //V put (K key , V value) //将对象存入到集合中,关联键值。key重复则覆盖原值
System.out.println("元素个数:"+map.size());
System.out.println(map);
//2. 删除
map.remove("usa");
System.out.println("删除之后:"+map.size());
//3. 遍历
//3.1 使用KeySet(); 和 增强for
System.out.println("========");
Set<String> keySet = map.keySet();
for (String key : keySet) {
//for (String key : map.KeySet())
System.out.println(key+":"+map.get(key));
}
System.out.println("========");
//3.2 使用entrySet()
//一个entry代表一个映射对,或者也叫一个键值对
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
// map.entrySet().for
// for (Map.Entry<String, String> entry : map.entrySet()) {
//
// }
//只需要取key,就用keySet来get(key),既取key又要value,就用map.entrySet();
//entrySet效率高于keySet
//4. 判断
System.out.println("========");
System.out.println(map.containsKey("cn"));
System.out.println(map.containsValue("泰国"));
}
}
3. Map集合的实现类
1. HashMap
- HashMap【重点】:
- JDK1.2,版本,线程不安全,运行效率快;允许用null作为key或是value
- 存储结构:哈希表(数组+链表+红黑树)
- 重复依据:键的hashCode()方法和equals方法
1. HashMap的使用
import java.util.HashMap;
import java.util.Map;
/**
* HashMap集合的使用
* 存储结构:哈希表(数组+链表+红黑树)
* 使用key存储,可重写hashcode和equals作为重复
*/
public class Demo02 {
public static void main(String[] args) {
//创建集合
HashMap<Student, String> students = new HashMap<Student,String>();
//1. 添加
Student s1 = new Student("孙悟空",100);
Student s2 = new Student("猪八戒",101);
Student s3 = new Student("沙和尚",102);
students.put(s1,"北京");
students.put(s2,"北京");
students.put(s3,"杭州");
students.put(s3,"南京");
students.put(new Student("沙和尚",102),"南京");
//默认对比的是内存地址,可重写equals方法
System.out.println("元素个数:"+students.size());
System.out.println(students.toString());
//2. 删除
students.remove(s1);
System.out.println("删除之后:"+students.size());
//3. 遍历
//3.1 使用KeySet();
System.out.println("========");
for (Student student : students.keySet()) {
System.out.println(student+":"+students.get(student));
}
//3.2 使用entrySet();
System.out.println("========");
for (Map.Entry<Student, String> entry : students.entrySet()) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
//4. 判断
System.out.println("========");
System.out.println(students.containsKey(s1));
System.out.println(students.containsKey(new Student("孙悟空",100)));
System.out.println(students.isEmpty());
}
}
import java.util.Objects;
public class Student {
private String name;
private int stuNo;
public Student() {
}
public Student(String name, int stuNo) {
this.name = name;
this.stuNo = stuNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStuNo() {
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", stuNo=" + stuNo +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return stuNo == student.stuNo &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, stuNo);
}
}
2. HashMap源码分析
1. 属性
- static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
hashMap初识容量大小 - static final int MAXIMUM_CAPACITY = 1 << 30;
hashMap的数组最大容量 - static final float DEFAULT_LOAD_FACTOR = 0.75f;
默认加载因子(容量达75%时扩容) - static final int TREEIFY_THRESHOLD = 8;
JDK1.8,当链表长度大于8时,调整成红黑树 - static final int UNTREEIFY_THRESHOLD = 6;
JDK1.8,当链表长度小于6时,调整成链表 - static final int MIN_TREEIFY_CAPACITY = 64;
JDK1.8,当链表长度大于8时,并且集合元素个数大于等于64时,调整成红黑树 - transient Node<K,V>[] table; //这里的table指的是哈希桶,面试必问
哈希表中的数组 - size //元素个数
2. 方法
- 构造方法
- 刚创建hashmap没填加元素时,table=null,size=0,目的节省空间
- 一添加即扩容为初识容量大小16
- 加载因子0.75时再次扩容
- put添加元素方法
- 先判断size,可resize方法扩容,后hash码值判断存放位置
3. 总结
- HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量调整为16
- 当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数
- JDK1.8,当链表长度大于8时,并且数组元素个数大于等于64时,调整成红黑树。目的是提高执行效率
- JDK1.8,当链表长度小于6时,调整成链表
- JDK1.8以前,链表是头插入,JDK1.8以后是尾插入(头插入多线程下可能会产生死链)
- hashSet与hashMap有异有同
- 实现了不同接口
- public class HashSet
extends AbstractSet
implements Set, Cloneable, java.io.Serializable - public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
- public class HashSet
- 同
- public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll©;
}
- public HashSet(Collection<? extends E> c) {
- 实现了不同接口
3. HashTable和Properties
- HashTable:
- JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value
- 现在已经不用了
- 子类:Properties【重点】
- 要求key和value都是String。通常用于配置文件的读取
- 和IO流关系比较大
2. TreeMap
- TreeMap:
- 实现了SortedMap接口(是Map的子接口),可以对key自动排序
1. TreeMap的使用
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Demo03 {
public static void main(String[] args) {
TreeMap<Student,String > treeMap = new TreeMap<Student,String>();
//定制比较的比较器(匿名内部类)
// TreeMap<Student,String > treeMap = new TreeMap<Student,String>(new Comparator<Student>() {
// @Override
// public int compare(Student o1, Student o2) {
// return 0;
// }
// });
//1. 添加
Student s1 = new Student("孙悟空",100);
Student s2 = new Student("猪八戒",101);
Student s3 = new Student("沙和尚",102);
treeMap.put(s1,"北京");
treeMap.put(s2,"上海");
treeMap.put(s3,"深圳");
treeMap.put(new Student("沙和尚",102),"南京");
System.out.println("元素个数:"+treeMap.size());
System.out.println(treeMap);
//2. 删除
// treeMap.remove(s3);
// System.out.println(treeMap.size());
// treeMap.remove(new Student("猪八戒",101));
// System.out.println(treeMap.size());
//3. 遍历
//3.1 使用keySet
System.out.println("========");
for (Student student : treeMap.keySet()) {
System.out.println(student+":"+treeMap.get(student));
}
//3.2 使用entrySet()
System.out.println("========");
for (Map.Entry<Student, String> entry : treeMap.entrySet()) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
//4. 判断
System.out.println(treeMap.containsKey(new Student("沙和尚",102)));
}
}
import java.util.Objects;
public class Student implements Comparable<Student>{
private String name;
private int stuNo;
public Student() {
}
public Student(String name, int stuNo) {
this.name = name;
this.stuNo = stuNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStuNo() {
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", stuNo=" + stuNo +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return stuNo == student.stuNo &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, stuNo);
}
@Override
public int compareTo(Student o) {
// int n1 = this.name.compareTo(o.getName());
int n = this.stuNo-o.getStuNo();
return n;
}
}
五、Collections工具类
- 概念:集合工具类,定义了除了存取以外的集合常用方法
- 方法:
- public static void reverse (List<?> list) //反转集合中元素的顺序
- public static void shuffle(List<?> list) //随机重置集合元素的顺序
- public static void sort(List list) //升序排序(元素类型必须实现Comparable接口)
import java.util.*;
/**
* Collections工具类的使用
*/
public class Demo04 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(20);
list.add(5);
list.add(12);
list.add(30);
list.add(6);
//sort排序
System.out.println("排序之前:"+list);
Collections.sort(list);
System.out.println("排序之后:"+list);
System.out.println("========");
//binarySearch
int i = Collections.binarySearch(list,12);
System.out.println(i);
int j = Collections.binarySearch(list,13);
System.out.println(j); //负的,表示没找到
System.out.println("========");
//copy复制
List<Integer> dest = new ArrayList<>();
for (int k = 0; k < list.size(); k++) {
dest.add(0);
}
Collections.copy(dest,list);
System.out.println(dest);
System.out.println("========");
//reverse反转
Collections.reverse(list);
System.out.println("反转之后:"+list);
System.out.println("========");
//shuffle打乱
Collections.shuffle(list);
System.out.println("打乱之后:"+list);
System.out.println("========");
//补充:
//list转成数组(散列)
Integer[] array = list.toArray(new Integer[0]);
System.out.println(array.length);
System.out.println(Arrays.toString(array));
//数组转成集合
String[] names = {"张三","李四","王五"};
List<String> list1 = Arrays.asList(names); //转成后的集合是一个受限集合,不能添加和删除
// List.add("赵六");
// List.remove(0);
System.out.println(list1);
int[] nums = {100,200,300,400,500};
List<int[]> list2 = Arrays.asList(nums);
System.out.println(list2); //有问题
//把基本类型数组转成集合时,需要修改为包装类型
Integer[] nums2 = {100,200,300,400,500};
List<Integer> list3 = Arrays.asList(nums2);
System.out.println(list3); //成功
}
}
六、总结
- 集合的概念:
- 对象的容器,和数组相似,定义了对多个对象进行操作的常用方法
- List集合:
- 有序、有下标、元素可以重复(ArrayList(数组)、LinkedList(双向链表)、Vector)
- Set集合:
- 无序、无下标、元素不可重复(HashSet(哈希表)、TreeSet(红黑树))
- Map集合:
- 存出一对数据,无序、无下标、键不可重复,值可重复(HashMap、HashTable(不用了)、TreeMap(Comparable接口))
- Collections:
- 集合工具类,定义了除了存取以外的集合常用方法
总结
即使再小的帆也能远航