集合容器
线程同步安全:
喂(Vector比arraylist多了个同步化机制线程安全)、S(Stack堆栈类,先进后出)、H(hashtable比hashmap多了个线程安全)、E(enumeration枚举,相当于迭代器)
HashTable privateMapmap=newHashtable<>();
SynchronizedMap privateMapmap= Collections.synchronizedMap(newHashMap());
ConcurrentHashMap 最推荐privateMapmap=newConcurrentHashMap<>();
5.1 Java集合容器框架
定义:sun公司发布的Java应用程序接口(API)的组成部分,放在java.util包。 公式:Java集合框架=集合接口+实现+算法。 数据结构:List列表、Queue队列、Stack栈、Set集、Map键值映射。实现时涉及红黑树、哈希表等数据结构。 上面图定义了6个接口,5个抽象类,8个实现类。其中:
- Collection接口是一组允许重复的对象。
- Set接口继承Collection,但不允许重复,使用自己内部的一个排列机制。
- List接口继承Collection,允许重复,以元素安插的次序来放置元素,不会重新排列。
- Map接口是一组成对的键-值对象,即所持有的是key-value pairs。
- Map中不能有重复的key。拥有自己的内部排列机制。
- 容器中的元素类型都为Object。从容器取得元素时,必须把它转换成原来的类型。

5.2 Collection接口
定义:Collection是最基本的集合接口,Collection中的元素是Object,Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用,可以使用子接口实现定义Collection。 注意:集合中只有对象,不能是基本数据类型。Collection不提供get()方法,遍历元素用iterator。
public class Student {
private String stuNo;//学号
private String stuName;//学生姓名
private int stuAge;//学生年龄
private String stuAddress;//学生地址
public Student() {
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public String getStuAddress() {
return stuAddress;
}
public void setStuAddress(String stuAddress) {
this.stuAddress = stuAddress;
}
}
//例题5_1Collection接口方法测试,对学生类对象进行添加、删除等操作。
public class Example5_1 {
public static void main(String[] args) {
//创建学生对象
Student stu1 = new Student();
stu1.setStuNo("0908233111");
stu1.setStuName("陈君祥");
stu1.setStuAge(21);
stu1.setStuAddress("江苏徐州");
Student stu2 = new Student();
stu2.setStuNo("0908233112");
stu2.setStuName("章家其");
stu2.setStuAge(22);
stu2.setStuAddress("江苏南京");
//创建集合对象
//ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,可以添加或删除元素。
//ArrayList 继承了 AbstractList ,并实现了 List 接口。
Collection collection = new ArrayList();
//add()添加对象
collection.add(stu1);
collection.add(stu2);
System.out.println("after add object:");
System.out.println("collection.size()=" + collection.size()); //size()判断集合中元素个数
// 取出对象
// 1定义迭代器对象collection.iterator()
// 2循环并判断选择对象iterator.hasNext()
// 3访问对象iterator.next()
Iterator iterator=collection.iterator();
while (iterator.hasNext()){
Object object=iterator.next();
Student student=(Student) object; //父对象->子对象
System.out.println("对象:"+student.getStuName());
}
//remove()删除对象
collection.remove(stu1);
System.out.println("after remove stu1 :");
System.out.println("collection.size()=" + collection.size());
//isEmpty()判断集合是否为空
boolean isEmpty = collection.isEmpty();
System.out.println("集合是否为空:" + isEmpty);
//contains()判断集合中是否包含某个对象
boolean isContains = collection.contains(stu2);
System.out.println("集合是否包含stu2:" + isContains);
//清除集合内对象
collection.clear();
System.out.println("afeter clear:");
System.out.println("collection.size()=" + collection.size());
}
}
5.3 Iterator迭代接口
定义:Iterator是迭代输出接口,就是将元素一个个进行判断,判断是否有内容,如果有内容则取出。
Iterator iterator=collection.iterator();
while (iterator.hasNext()){}
//例题5_2使用Iterator遍历对象进行集合遍历
public class Example5_2 {
public static void main(String[] args) {
// 取出对象
// 1定义迭代器对象collection.iterator()
// 2循环并判断选择对象iterator.hasNext()
// 3访问对象iterator.next()
//创建集合对象
Collection collection = new ArrayList();
List list=new ArrayList();
//添加集合元素
collection.add("stu1");
collection.add("stu2");
collection.add("stu3");
//获得一个迭代器对象:iterator
Iterator iterator = collection.iterator();
//iterator.hasNext()判断是否存在另一个可访问的元素
while (iterator.hasNext()) {//遍历
//iterator.next()返回要访问的下一个元素 返回Object。到达集合结尾,则抛出NoSuchElementException
Object element = iterator.next();
System.out.println("iterator = " + element);
}
if (collection.isEmpty()) {
System.out.println("collection is Empty!");
} else {
System.out.println("collection is not Empty! size="
+ collection.size());
}
//获得一个迭代器对象:iterator2
Iterator iterator2 = collection.iterator();
while (iterator2.hasNext()) {//移除元素
Object element = iterator2.next();
System.out.println("remove: " + element);
//删除上次访问返回的对象,本方法必须紧跟在一个元素的访问后。
// 上次访问后集合被修改,抛出错误IllegalStateException。
iterator2.remove();
}
//获得一个迭代器对象:iterator3
Iterator iterator3 = collection.iterator();
if (!iterator3.hasNext()) {//察看是否还有元素
System.out.println("没有元素了");
}
//判断集合是否为空
if (collection.isEmpty()) {
System.out.println("collection is Empty!");
}
}
}
5.4 List接口
定义:List列表容器是Collection的子接口,对象按次序排列,允许内容重复,可以产生ListIterator,双向遍历对象。 实现类:ArrayList、LinkedList、Vector、Stack等。
public class Student {
private String name;
private int score;
public Student() {
super();
}
public Student(String name, int score) {
super();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
//输出此对象时,默认直接调用了
public String toString() {
String s = name + " 成绩:" + score;
return s;
}
}
//例5_6 List及ListIterator接口的使用
public class Example5_6 {
List list1 = new LinkedList();
List list2 = new ArrayList();
ListIterator it;
//无参构造
public Example5_6() {
Student ZhangSan = new Student("张三", 90);
// 顺序插入元素
System.out.println("-----------[演示1] 顺序插入元素---------------------");
list1.add(0, ZhangSan);
list1.add(1, "张三");
list1.add(2, "李四");
list1.add(3, new Student("王武", 85));
list1.add(4, new Student("赵榴", 76));
list1.add(5, ZhangSan);
printCollection(list1);
// 删除元素(对于LinkedList 建议使用迭代器遍历删除)
System.out.println("-----------[演示2] 删除元素---------------------");
it = list1.listIterator();
while (it.hasNext()) {
Object o = it.next();
if (o instanceof String) {
System.out.println("String 对象 [ " + o + " ] ——从列表中清除!");
it.remove();
}
}
// //使用循环遍历时,要考虑删除元素后的索引变化,因此需要使用逆序循环。
// for (int i=5;i>-1;i--) {
// if (list1.get(i) instanceof String){
// System.out.println("String 对象 [ "+list1.remove(i)
// +" ] ——从列表中清除!");
// }
// }
printCollection(list1);
// 逆序插入元素 将元素加到尾部
System.out.println("-----------[演示3] 逆序插入元素--------------------");
list2.add(0, ZhangSan);
list2.add(0, "李四");
printCollection(list2);
// 插入列表 从index=0开始,将list1列表元素加入list2中
System.out.println("-----------[演示4] 插入列表---------------------");
list2.addAll(0,list1);
printCollection(list2);
// 定位元素
System.out.println("-----------[演示5] 定位元素---------------------");
System.out.println("首个 [ " + ZhangSan + " ] 对象位于"
+ list2.indexOf(ZhangSan));
System.out.println("末个 [ " + ZhangSan + " ] 对象位于"
+ list2.lastIndexOf(ZhangSan));
// 截取子列表
System.out.println("-----------[演示6] 截取子列表---------------------");
// System.out.println("1:"+list1.size()); //列表1长度
// System.out.println("2:"+list2.size());
list1 = list2.subList(1,5);
printCollection(list1);
}
//输出集合方法
private void printCollection(List list) {
it = list.listIterator();
int n = 0;
while (it.hasNext()) {
System.out.println(n + ":" + it.next());
n++;
}
}
public static void main(String[] args) {
new Example5_6(); //实例化 调用无参构造
}
}
5.5 ArrayList类(实现不同步、线程不安全)
定义:ArrayList是List接口的实现类。大小可变的动态数组列表。允许null元素。 注意:此实现不是同步的,多个线程同时访问一个ArrayList时,至少一个从结构上修改了它,它必须保持外部同步。 _ArrayList_实现了_List_接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现
//例题5_3ArrayList类的方法测试
public class Example5_3 {
public static void main(String[] args) {
// 创建ArrayList对象
List list = new ArrayList();
// 将指定的元素添加到此列表的尾部
list.add("王小二");
list.add("张小三");
list.add("李小四");
list.add("陈小六");
list.add("赵小八");
System.out.println("list.size=" + list.size());
// 将指定的元素插入此列表中的指定位
list.add(2, "孙小五");
System.out.println("after insert 孙小五");
System.out.println("list.size=" + list.size());
// 返回此列表中指定位置上的元素
String user = (String) list.get(2);
System.out.println("user=" + user);
// 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1
int index = list.indexOf("张小三");
System.out.println("index=" + index);
// 返回此列表中最后一次出现的指定元素的索引,如果列表不包含索引,则返回 -1。
int lastIndex = list.lastIndexOf("张小三");
System.out.println("lastIndex=" + lastIndex);
// 如果此列表中没有元素,则返回 true
System.out.println("列表是否为空:" + list.isEmpty());
// 移除此列表中指定位置上的元素
list.remove(2);
System.out.println("after remove index=2");
System.out.println("list.size=" + list.size());
// 移除此列表中首次出现的指定元素(如果存在)
list.remove("张小三");
System.out.println("after remove 张小三");
System.out.println("list.size=" + list.size());
// 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
System.out.println("list.toArray遍历****");
Object[] users = (Object[]) list.toArray();
for (int i = 0; i < users.length; i++) {
System.out.print(users[i] + " ");
}
System.out.println();
// ArrayList遍历方法一
System.out.println("ArrayList中元素遍历方法一list.get(i)****");
for (int i = 0; i < list.size(); i++) {
// 返回此列表中指定位置上的元素
String userName = (String) list.get(i);
System.out.print(userName + " ");
}
// ArrayList遍历方法二 iterator列表迭代器
System.out.println("\nArrayList中元素遍历方法二list.iterator()*****");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String userName = (String) iterator.next();
System.out.print(userName + " ");
}
System.out.println("\n清除列表中所有元素******");
//清除列表中所有元素
list.clear();
System.out.println("after clear list");
System.out.println("list.size=" + list.size());
}
}
5.6 LinkedList类(实现不同步、线程不安全)
定义:LinkedList是List接口的链接列表实现类。可以用作Stack堆栈、Queue队列或双端队列。 区别:ArrayList允许随机访问,插入删除效率差。LinkedList最佳适合遍历,不适合随机访问,插入删除效率较高。 关于栈或队列,现在的首选是_ArrayDeque_,它有着比_LinkedList_(当作栈或队列使用时)有着更好的性能。 _LinkedList_底层通过双向链表实现
/**
* 例题5_4运用LinkedList 链接列表 模拟栈结构(Stack)
* 插入、删除的一端为栈顶Top。另一端为栈底Bottom。栈为后进先出的线性表-LIFO表。
*/
public class Example5_4 {
// 创建LinkedList对象 private私有作用域,只能在本类中有效。
private LinkedList list = new LinkedList();
// 入栈 每加一个之前,整体往右移动一个位置
public void push(Object o) {
list.addFirst(o);
}
// 出栈 栈顶为开头元素
public Object pop() {
return list.removeFirst();
}
// 获取栈顶元素
public Object peek() {
return list.getFirst();
}
// 栈是否为空
public boolean empty() {
return list.isEmpty();
}
public static void main(String[] args) {
//因为方法不是类方法,所以要实例化后,才能被使用
Example5_4 stack = new Example5_4();
// 入栈
stack.push("王小二"); //第1个进栈,作为栈底。
stack.push("张小三");
stack.push("李小四"); //最后1个进栈,作为栈顶。
// 出栈
System.out.println("出栈元素:" + stack.pop());
// 显示栈顶元素
System.out.println("栈顶元素:" + stack.peek());
// 出栈
System.out.println("出栈元素:" + stack.pop());
// 显示栈顶元素
System.out.println("栈顶元素:" + stack.peek());
System.out.println("栈是否为空:" + stack.empty());
}
}
/**
* 例题5_6 运用LinkedList模拟队列结构(Queue)
* 队列只允许在一端插入-队尾Rear,而在另一端删除-对头Front的运算受限的线性表。队列为先进先出-FIFO表。
*/
public class Example5_5 {
// 创建LinkedList对象 私有
private LinkedList list = new LinkedList();
// 入队 每加一个之前,整体往左移动一个位置
public void put(Object o) {
list.addLast(o);
}
// 出队:使用removeFirst()方法,返回队列中第一个数据,然后将它从队列中删除
public Object get() {
return list.removeFirst();
}
// 队列是否为空
public boolean empty() {
return list.isEmpty();
}
public static void main(String[] args) {
Example5_5 queue = new Example5_5();
// 入队
queue.put("王小二"); //第一个入队列,为队头
queue.put("张小三");
queue.put("李小四"); //最后一个入队列,为队尾
// 出队
System.out.println("出队元素:" + queue.get());
System.out.println("出队元素:" + queue.get());
System.out.println("出队元素:" + queue.get());
// 判断队列是否为空
System.out.println("队列是否为空:" + queue.empty());
}
}
5.7 Stack类(实现同步、线程安全)
定义:Stack堆栈是Vector的一个子类,表示后进先出(LIFO)的对象堆栈。 它通过五个操作扩展了类Vector ,允许将向量视为堆栈。 提供了通常的push和pop操作,以及在堆栈顶部项目中的peek的方法,用于测试堆栈是否为empty的方法,以及用于项目的堆栈的方法以及发现它的距离search是从顶部。
Deque接口及其实现提供了更完整和一致的LIFO堆栈操作集,应优先使用此类。 例如:
Deque<Integer> stack = new ArrayDeque<Integer>();
//例题5_7 向堆栈Stack中添加元素并弹出
public class Example5_7 {
public static void main(String[] args) {
Stack stack1 = new Stack();// 构造一个空堆栈stack1
try {
stack1.push(new Integer(0)); //堆栈底
stack1.push(new Integer(1));
stack1.push(new Integer(2));
stack1.push(new Integer(3));
stack1.push(new Integer(4)); //堆栈顶
System.out.print((Integer) stack1.pop()+"\n"); //默认输出时,末尾是没有换行符的。
System.out.print(" "+(Integer) stack1.pop());
System.out.print(" "+(Integer) stack1.pop());
System.out.print(" "+(Integer) stack1.pop());
System.out.print(" "+(Integer) stack1.pop()); //pop返回栈顶元素并删除
System.out.print(stack1.empty()); //peek只返回栈顶元素,在堆栈为空时,抛出异常。
} catch (EmptyStackException e) {
e.printStackTrace();
}
}
}
Java中_PriorityQueue_实现了_Queue_接口,不允许放入null元素;其通过堆实现,具体说是通过完全二叉树(complete binary tree)实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值),也就意味着可以通过数组来作为_PriorityQueue_的底层实现。
5.8 Vector类(实现同步、线程安全)
定义:Vector提供向量(Vector)类以实现类似动态数组的功能,与ArrayList相似,但Vector的线程是安全同步的。Vector类实现了可增长的对象数组。 像数组一样,它包含可以使用整数索引访问的组件。 但是 Vector的大小可以根据需要增大或缩小,以便在创建Vector后添加和删除项目。
5.9 Set接口
定义:Set接口是Collection的子接口,不允许集合中有重复项,依赖equals()方法检查独一性。 实现类:HashSet、TreeSet、LinkedHashSet
5.10 hashCode和equal方法

- hashCode方法 定义:在Java中,哈希码代表对象的特征,hashCode()是用来计算哈希值的。主要用于帮助集合中元素的定位。哈希码并不完全唯一,它是一种算法。
- equals方法 定义:equals是判断2个对象是否相等的方法,在Object类里有实现,判断的是2个对象的内存地址。 equals和“==”的区别:equals是指内容相等,==是指地址相等,都来自于Object。没有重写equals方法的类,它们是一样的。基础数据类型和重写equals方法的类,则不是。
- 重写hashcode和equals方法 定义:先hashCode方法,哈希值不同,则不需调用equals方法。哈希值一样,再调用equals方法继续比较。equals相同,说明元素存在。equals不同,在散列一次计算该元素的hashCode(),若此时位置为空,说明元素不存在。若存在元素,在进行eqauls,反复下去。 hashcode和equals的关系 equals相等,hashCode必须相等。equals不相等,hashCode有可能会相等。hashCode不相等,equals肯定不相等。
public class Point extends Object{
private double x;
private double y;
public Point() {
super(); //调用父类的无参构造
}
public Point(double x, double y) {
super(); //调用父类的有参构造
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
@Override
public String toString() {
return this.x + " " + this.y;
}
//重写hashCode方法-使用hashCode方法计算哈希值???????
//这里重写的是Object类的hashCode()
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
//计算哈希码值
temp = Double.doubleToLongBits(x);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
//重写equals方法-判断x与y是否相等-内容相等???????????
//这里重写的是Object类的equals(Object obj)
@Override
public boolean equals(Object obj) {
if (this == obj) //this代表对象Point
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Point other = (Point) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
return false;
return true;
}
}
/**
* 例题5_8重写hashcode及equals方法测试
*/
public class Example5_8 {
public static void main(String[] args)
{
//实例化数组列表 允许重复,按特定顺序排列
ArrayList al=new ArrayList();
Point rp1=new Point(3,3);
Point rp2=new Point(5,6);
Point rp3=new Point(3,3);
al.add(rp1);
al.add(rp2);
al.add(rp3);
al.add(rp1);
System.out.println("list:"+al.size());//list: 4
System.out.println(al);
//实例化Set集合 不允许重复,不按特定顺序排列
Set hs=new HashSet();
hs.add(rp1);
hs.add(rp2);
hs.add(rp3);
hs.add(rp1);
//重写了hashCode方法和equals方法,rp1重复添加了,只添加一个rp1,rp1和rp3也重复,不添加rp3
System.out.println("hs:"+hs.size()); //hs:2 [集合内存放的是rp1和rp2]
System.out.println(hs);//[3 3, 5 6]
//问题?????????????
//集合中添加了元素rp1后,修改对象rp1的成员变量值,这个成员变量是参与了hashCode()值的运算,进行移除rp1操作
rp1.setX(100.0); //因为对rp1修改,删除,新增,都会修改hashCode值。修改后就不能移除原来的rp1对象。
hs.remove(rp1);//移除元素,移除失败。
// 但可以放在修改前移除,就可以成功移除。
//发现还是2个元素,remove方法因为哈希值被修改了,无法移除原来的rp1对象,造成内存泄露!!
System.out.println(hs);
}
}
5.11 HashSet类-哈希值(实现不同步、线程不安全)
定义:HashSet类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。允许null元素。不保证Set的迭代顺序,不保证顺序恒久不变。hashset类实现不是同步的 Java8 对 HashSet 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成。 前面已经说过_HashSet_是对_HashMap_的简单包装,对_HashSet_的函数调用都会转换成合适的_HashMap_方法,因此_HashSet_的实现非常简单,只有不到300行代码。
红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。 [1] 红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。 [2] 它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。 [2] 红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。
/**
* //例题5_9HashSet类的方法测试
*/
public class Example5_9 {
public static void main(String[] args) {
// 创建HashSet对象
Set set = new HashSet();
// set中添加元素
set.add("Aaron");
set.add("Abel");
set.add("Adam");
//输出set中元素数目
System.out.println("set.size=" + set.size());
//添加一个已存在元素,不会报错,只是添加无效。
set.add("Abel");
System.out.println("添加与存在的元素后的set.size=" + set.size());
// HashSet元素的遍历
Iterator iterator = set.iterator();
System.out.println("set集合中的元素是:");
//循环是否存在另一个可访问的元素iterator.hasNext()
while (iterator.hasNext()) {
//返回要访问的下一个元素iterator.next()
System.out.print(iterator.next() + " ");
}
}
}
5.12 Comparble自比较接口
定义:Comparable接口适用于一个有自然顺序的类,把同一类型的集合排序成自然顺序。 注意:若元素所在类实现类Comparable接口,可以直接使用Collections或Arrays类的sort方法对集合或数组排序。
//模型类实现Comparable接口 把集合排序成自然排序
public class Customer implements Comparable {
private String name;
private int age;
public Customer(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
// 重写equals方法
if (this == obj) //地址比对
return true;
if (!(obj instanceof Customer))
return false;
final Customer other = (Customer) obj;
if (this.name.equals(other.getName()) && this.age == other.getAge()) //内容比对
return true;
else
return false;
}
//重写Comparable接口下的compareTo(Object o)方法
//把集合排序成自然顺序,从大到小,从长到短。
public int compareTo(Object o) {
Customer other = (Customer) o;
// 先按照name属性排序
if (this.name.compareTo(other.getName()) > 0)
return 1; //在o后面
if (this.name.compareTo(other.getName()) < 0)
return -1; //在o前面
// 再按照age属性排序
if (this.age > other.getAge())
return 1; //在o后面
if (this.age < other.getAge())
return -1; //在o前面
return 0; //位置相同
}
//hashCode()方法,提供快速定位集合中元素的位置,提高查询/插入速度。
@Override
public int hashCode() {
// 重写equals方法必须重写hashCode方法
int result;
//name不为空 取出哈希值
result = (name == null ? 0 : name.hashCode());
result = 29 * result + age;
return result;
}
//输出Customer对象时,自动调用toString方法输出相关内容
public String toString() {
return "姓名:" + this.getName() + " 年龄:" + this.getAge();
}
}
/**
* 例题5_10Comparable接口使用测试-静态绑定排序
* 把集合中元素排成自然顺序,先名称、再年龄
*/
public class Example5_10 {
public static void main(String[] args) {
Set<Customer> set = new HashSet<Customer>();
//compareTo(Object o)方法把集合排序成自然顺序,从大到小,从长到短。
Customer customer1 = new Customer("Tom", 17);
Customer customer2 = new Customer("Tony", 16);
Customer customer3 = new Customer("Tony1", 15);
set.add(customer1);
set.add(customer2);
set.add(customer3);
System.out.println(set.size());
//System.out.println(customer1.compareTo(customer2)); //返回-1,表示目前对象1在对象2前面(自然排序前)
System.out.println(set);
}
}
5.13 TreeSet类-树结构(实现不同步、线程不安全)
定义:TreeSet类实现Set接口,还实现了java.util.SortedSet接口,保证遍历集合时按照递增的顺序获取对象。 注意:实现Set集合中的对象,必须实现Comparable接口,也可以指定比较器递增排序。肯定可以比较对象大小,从而实现对象排序。
TreeSet底层通过红黑树(Red-Black tree)实现,也就意味着containsKey(), get(), put(), remove()都有着log(n)的时间复杂度。 说_TreeSet里面有一个TreeMap_(适配器模式)
/**
* 例题5_11TreeSet类的方法测试-树型集合
*/
public class Example5_11 {
public static void main(String[] args) {
/**
* 创建TreeSet对象,它不仅是实现了Set接口,还是实现了java.util.SortedSet接口,遍历集合时递增顺序获取对象。
*/
Set set = new TreeSet();
// set中添加元素 添加的对象必须实现Comparable接口,也可以是指定比较其递增排序。不然抛出RuntimeException错误
set.add("jack");
set.add("alen");
set.add("rose");
set.add("black");
set.add("helo");
System.out.println("set.size=" + set.size());
// TreeSet中元素遍历(默认从小到大) TreeSet类让遍历集合时递增顺序获取对象(这里是按照首字母顺序排列)
Iterator iterator = set.iterator();
System.out.println("显示set中所有元素");
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
}
}
5.14 Map接口
定义:Map是一种键对象和值对象关联的容器,键不重复,值可重复,没有继承Collection接口。值对象可以是Map,以此类推,实现多级映射。 Map提供三种集合视图,1一组key集合、2一组value集合、3一组key-value映射。 实现类:HashMap、Hashtable、HashMap子类LinkedHashMap、SortedMap子接口及其实现类TreeMap。 Map内部类Entry,封装了一个key-value对。Map是特殊的Set,不过元素是Entry对象Map.Entry<K,V>。
5.15 HashMap类(实现不同步、线程不安全)
定义:HashMap类是基于哈希表的Map接口的实现,允许使用null值和null键。不保证映射的顺序,不保证顺序恒久不变。用到了哈希值算法。 Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成。 可将_LinkedHashMap_看作采用_linked list_增强的_HashMap_。
public class Student {
private String no;
private String name;
private int age;
private String address;
public Student() {
//但没有继承类时,每个类默认继承Object类。super()调用Object类的无参构造方法
super();
}
public Student(String no, String name, int age, String address) {
super();
this.no = no;
this.name = name;
this.age = age;
this.address = address;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
//重写hashCode方法-使用hashCode方法为Student对象计算哈希值
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((no == null) ? 0 : no.hashCode());
return result;
}
//重写equals方法-判断x与y是否相等-内容相等
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (no == null) {
if (other.no != null)
return false;
} else if (!no.equals(other.no))
return false;
return true;
}
@Override
public String toString() {
return "学生学号: " + this.getNo() + "学生姓名: " + this.getName()
+ "学生年龄: " + this.getAge() + "学生地址: " + this.getAddress();
}
}
/**
* 例题5_12HashMap类的方法测试
*/
public class Example5_12 {
public static void main(String[] args) {
//创建HashMap对象
Map map=new HashMap();
//创建学生对象
Student stu1=new Student();
stu1.setNo("0908001");
stu1.setName("张阳");
stu1.setAge(20);
stu1.setAddress("江苏徐州");
Student stu2=new Student();
stu2.setNo("0908002");
stu2.setName("孙旭");
stu2.setAge(19);
stu2.setAddress("江苏常州");
Student stu3=new Student();
stu3.setNo("0908003");
stu3.setName("李东");
stu3.setAge(21);
stu3.setAddress("江苏苏州");
//以stuNo为key,student对象为value保存中map中 这里按年龄进行递增排序
map.put(stu1.getNo(), stu1);
map.put(stu2.getNo(), stu2);
map.put(stu3.getNo(), stu3);
System.out.println(map); //Student.toString方法输出格式
//containsKey:如果此映射包含 对于指定键 的映射关系,则返回 true。
boolean isContainsKey=map.containsKey(stu1.getNo());
System.out.println("根据学生学号判断是否包含学生一:"+isContainsKey);
//containsValue:如果此映射将一个或多个键映射到 指定值 ,则返回 true。
boolean isContainsValue=map.containsValue(stu2);
System.out.println("根据学生对象值判断是否包含学生二:"+isContainsValue);
//get(Object key) :返回指定键所映射的值。
Student stu=(Student)map.get(stu3.getNo());
System.out.println("stuName="+stu.getName());
//entrySet() :返回此映射所包含的映射关系key-value的 Set 视图。
Set entry=map.entrySet();
Iterator iterator=entry.iterator();
System.out.println("映射关系");
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//keySet() :返回此映射中所包含的键key的 Set 视图。
Set keys=map.keySet();
System.out.println("map中的key");
Iterator iterator1=keys.iterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
}
//遍历HashMap中values 返回所有value组成的collection
Collection stuValues=map.values();
System.out.println("map中学生对象");
Iterator iterator2=stuValues.iterator();
while(iterator2.hasNext()){
Student student=(Student)iterator2.next();
System.out.println(student.getNo()+":"+student.getName()+":"
+student.getAge()+":"+student.getAddress());
}
//remove(Object key) :从此映射中移除指定键的映射关系(如果存在)。
System.out.println("删除前 的 map.size="+map.size());
map.remove(stu2.getNo());
System.out.println("删除后的 map.size="+map.size());
//clear() :从此映射中移除所有映射关系。 size()表示key-value对的个数
System.out.println("清除前 的 map.size="+map.size());
map.clear();
System.out.println("清除后的 map.size="+map.size());
}
}
5.16 TreeMap类(实现不同步、线程不安全)
定义:TreeMap类通过使用树实现Map接口。提供了按排序顺序存储关键字/值对的方法,元素按照关键字升序排序,允许快速检索。 _TreeMap_底层通过红黑树(Red-Black tree)实现,也就意味着containsKey(), get(), put(), remove()都有着log(n)的时间复杂度。
5.17 Comparator接口
定义:Comparator是自定义指定比较器的自比较接口,提供自然排序功能-从大到小。 区别:Comparable要和具体要进行排序的类的实例绑定,静态绑定排序,只能实现该接口一次。Comparator通过构造方法指定一个比较器可以实现,自定义比较方式,提供多种排序方式,动态绑定排序。
//定义动态比较类CollatorComparator,实现Comparator动态比较接口,定义自己的比较方式。
public class CollatorComparator implements Comparator {
/** Collator类简介
* Collator 类执行区分语言环境的 String 比较。使用此类可为自然语言文本构建搜索和排序例程。
* Collator 主要处理:规范化的典型等效 字符、多层次的比较。
* 主要是用来对区域敏感性的字符串比较的,对本地化字符串进行排序。
* Collator 是一个抽象基类。其子类实现具体的整理策略。Java 平台目前提供了RuleBasedCollator子类,它适用于很多种语言。还可以创建其他子类,以处理更多的专门需要。
* 与其他区分语言环境的类一样,可以使用静态工厂方法 getInstance 来为给定的语言环境获得适当的 Collator 对象。如果需要理解特定整理策略的细节或者需要修改策略,只需查看 Collator 的子类即可。*/
//https://blog.csdn.net/u013249965/article/details/52507343
// Collator.getInstance():获得当前默认语言环境的 Collator。
Collator collator = Collator.getInstance();
//实例化 Collator,并获取当前语言环境,通过重写比较器 Comparator。比较两个参数,返回比较结果。
public int compare(Object element1, Object element2) {
CollationKey key1 = collator.getCollationKey(element1.toString());
CollationKey key2 = collator.getCollationKey(element2.toString());
return key1.compareTo(key2);
}
}
/**
* 例题5_13TreeMap类的方法测试-动态绑定排序
* 提供了按排序顺序存储关键字/值对的方法,元素按照关键字升序排序,允许快速检索。
*/
public class Example5_13 {
public static void main(String[] args) {
//创建一个比较对象CollatorComparator
CollatorComparator comparator = new CollatorComparator();
//创建TreeMap对象 使用树实现Map接口,提供了按排列顺序存储key-value对的方法,允许快速检索。
//元素按照 关键字 默认升序排序。
TreeMap map = new TreeMap(comparator);
//添加数字元素到map中
for (int i = 0; i < 10; i++) {
String s = "" + (int) (Math.random() * 1000);
map.put(s, s);
}
//添加字符字符串到map中
map.put("abcd", "abcd");
map.put("Abc", "Abc");
map.put("bbb", "bbb");
map.put("BBBB", "BBBB");
map.put("eeee", "eeee");
//添加汉字字符串到map中
map.put("北京", "北京");
map.put("中国", "中国");
map.put("上海", "上海");
map.put("厦门", "厦门");
map.put("香港", "香港");
//遍历TreeMap对象
Set set=map.entrySet();
Iterator it = set.iterator();
int count = 1;
while(it.hasNext())
{
Map.Entry entry=(Map.Entry)it.next();
//数字排在最前,英文字母其次,汉字则按照拼音进行排序。混合元素集合排序。
System.out.println("映射的键是: "+entry.getKey()+" 值是:"+entry.getValue());
if (count % 5 == 0) {
System.out.println("");
}
count++;
}
}
}
实践2
//定义动态比较类CollatorComparator,实现Comparator动态比较接口,定义自己的比较方式。
public class StudentComparator implements Comparator<Student> {
//根据学生年龄比较 stu1在stu2前为-1,后为1,等为0
public int compare(Student stu1, Student stu2) {
if (stu1.getAge() > stu2.getAge()) {
return 1;
} else if (stu1.getAge() < stu2.getAge()) {
return -1;
}
return 0;
}
}
/**
* 例题5_14 Comparator接口的方法测试_根据学生年龄排序
*/
public class Example5_14 {
public static void main(String[] args) {
// 创建学生对象
Student stu1 = new Student();
stu1.setName("one");
stu1.setAge(18);
Student stu2 = new Student();
stu2.setName("two");
stu2.setAge(20);
Student stu3 = new Student();
stu3.setName("three");
stu3.setAge(19);
// 创建学生对象数组
Student[] students = { stu1, stu2, stu3 };
// 显示排序前的学生对象数组
System.out.println("排序前学生信息");
for (int i = 0; i < students.length; i++) {
Student stu = (Student) students[i];
System.out.print(stu.getName() + ":" + stu.getAge() + " ");
}
System.out.println("");
// 根据学生年龄排序 Arrays的排序函数sort(数据,自定义的Comparator动态比较接口实现类)
Arrays.sort(students, new StudentComparator()); //递增
// 显示排序后的学生对象数组
System.out.println("排序后学生信息");
for (int i = 0; i < students.length; i++) {
Student stu = (Student) students[i];
System.out.print(stu.getName() + ":" + stu.getAge() + " ");
}
}
}
5.18 Collections类
定义:Collections是针对集合类的一个工具类,提供了一系列静态方法实现对各种集合的排序、搜索和线程安全等操作。有许多方便的方法可以使用。主要针对List和Set集合 注意:Collections不能创建对象,由在Colletion上进行操作或返回Collection的静态方法组成。
/**
* 例题5_15 Collections集合类的工具 使用测试
* 主要针对List和Set集合
*/
public class Example5_15 {
public static void main(String[] args) {
//实例化一个链接列表,存放String类型数据
LinkedList<String> list = new LinkedList<String>();
list.add("1");
list.add("2");
list.add("3");
// 创建一个逆序的比较器-自定义
Comparator<String> r = Collections.reverseOrder();
// 通过逆序的比较器进行排序
Collections.sort(list, r);
System.out.println("逆序的比较器进行排序******");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("反顺序******");
// 反顺序-上面反、这里反——得正
Collections.reverse(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("打乱顺序******");
// 打乱顺序
Collections.shuffle(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("输出最大和最小的数******");
// 输出最大和最小的数
System.out.println(Collections.max(list) + ":" + Collections.min(list));
}
}
队列和栈创建使用
/**
* @author cmy
* @version 1.0
* @date 2022/7/5 0005 16:05
* @description 队列和栈
*/
public class QueueStack {
/**
* 队列练习:先进先出
*
* LinkedList底层用的双向链表,下标操作线性时间,头尾删除常数时间,没有实现同步synchronized。
* LinkedList同时实现了List接口和Deque接口,即可做队列,也可做栈。
* 关于栈或队列,首选是ArrayDeque,底层通过循环数组实现,比LinkedList(当作栈或队列使用时)更好性能
* https://blog.csdn.net/tyh1579152915/article/details/118529597
*/
public static void QueueTest(){
//add()和remove()方法在失败的时候会抛出异常(不推荐)
Queue<String> queue = new ArrayDeque<String>();
//入队:添加元素
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
//循环输出_出队
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
//出队:返回并删除此队列的头部,如果此队列为空,则返回 null
System.out.println("poll="+queue.poll());
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
//返回不删除此队列的头部,为空不抛异常
System.out.println("element="+queue.element());
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
//返回不删除此队列的头部,为空抛异常
System.out.println("peek="+queue.peek());
for(String q : queue){
System.out.println(q);
}
}
/**
* 入栈
* @param st
* @param a
*/
static void showpush(Deque<Integer> st, int a) {
st.push(new Integer(a));
System.out.println("push(" + a + ")");
System.out.println("stack: " + st);
}
/**
* 出栈
* @param st
*/
static void showpop(Deque<Integer> st) {
System.out.print("pop -> ");
Integer a = (Integer) st.pop();
System.out.println(a);
System.out.println("stack: " + st);
}
/**
* 栈练习:后进先出
*
* 栈的重点应该在于 栈先进后出的思想,以及对 入栈(push),出栈(pop) 两种操作的运用。
* 解决相关括号匹配,迷宫求解,表达式求值等问题,都是一个不错的选择。
* https://blog.csdn.net/qq_42124842/article/details/91420306
*/
public static void StackTest(){
////效率低、线程安全
//Stack<Integer> st = new Stack<Integer>();
//效率高、线程不安全
Deque<Integer> st=new ArrayDeque<Integer>();
System.out.println("stack: " + st);
showpush(st, 42);
showpush(st, 66);
showpush(st, 99);
showpop(st);
showpop(st);
showpop(st);
//stack为空抛异常
try {
showpop(st);
} catch (EmptyStackException e) {
System.out.println("empty stack");
}
}
public static void main(String[] args) {
//队列
//QueueTest();
//栈
StackTest();
}
}