集合
1.Collection
1.1 集合概述
集合类的特点:提供一种存储空间可变的存储类型,存储的数据容量可以随时发生改变
1.2 体系结构
1.3 Collection概述和使用
Collection集合概述
- 是单列集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collection;
/*
创建Collection集合对象
多态的方式
ArrayList
*/
public class CollectionDemo01 {
public static void main(String[] args) {
//创建Collection集合的对象
Collection<String> c1 = new ArrayList<String>();
//添加元素:boolean add(E e)
c1.add("hello");
c1.add("world");
c1.add("java");
//输出集合对象
System.out.println(c1);
}
}
1.4 Collection常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定元素 |
void clear() | 清空集合中元素 |
boolean contains(Object o) | 判断集合中是否存在指定元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素个数 |
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collection;
//Collection常用方法
public class CollectionDemo02 {
public static void main(String[] args) {
//创建集合对象
Collection<String> c1 = new ArrayList<String>();
//boolean add(E e):添加元素
System.out.println(c1.add("java"));//true,调用add方法永远返回true
System.out.println(c1.add("hello"));//true
System.out.println(c1.add("hello"));//true
//输出集合对象
System.out.println(c1);//[java, hello, hello]
//boolean remove(Object o):从集合中移除指定元素
System.out.println(c1.remove("hello"));//true
System.out.println(c1.remove("javase"));//false
System.out.println(c1);//[java, hello]
//void clear():清空集合中元素
// c1.clear();
System.out.println(c1);//[]
//boolean contains(Object o):判断集合中是否存在指定元素
System.out.println(c1.contains("java"));//true
System.out.println(c1.contains("javase"));//false
//boolean isEmpty():判断集合是否为空
System.out.println(c1.isEmpty());//false
// c1.clear();
System.out.println(c1.isEmpty());//true
//int size():集合的长度,也就是集合中元素个数
System.out.println(c1.size());//2
}
}
1.5 Collection集合遍历
Iterator:迭代器,集合的专用遍历方式
- Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
- 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
Iterator中常用方法
- E next():返回迭代中的下一个元素
- boolean hasNext():如果迭代具有更多元素,则返回true
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
//Iterator迭代器
public class IteratorDemo01 {
public static void main(String[] args) {
//创建集合对象
Collection<String> c1 = new ArrayList<String>();
//添加元素
c1.add("hello,world!");
c1.add("java,javase,javaee");
System.out.println(c1);//[hello,world!, java,javase,javaee]
//Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
Iterator<String> it = c1.iterator();
//E next():返回迭代中的下一个元素
// System.out.println(it.next());//hello,world!
// System.out.println(it.next());//java,javase,javaee
// System.out.println(it.next());//NoSuchElementException
//boolean hasNext():如果迭代具有更多元素,则返回true
// System.out.println(it.hasNext());//false
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
1.6 案例
Collection集合存储学生对象并遍历
需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
//案例:Collection集合存储学生对象并遍历
public class CollectionDemo03 {
public static void main(String[] args) {
//创建集合对象
Collection<Student> c = new ArrayList<Student>();
//创建学生对象
Student s1 = new Student("罗沙雕", 20);
Student s2 = new Student("陈大锤", 25);
Student s3 = new Student("苏斩妹", 28);
//添加学生对象到集合
c.add(s1);
c.add(s2);
c.add(s3);
//迭代器遍历集合
Iterator<Student> it = c.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
//Student类
class Student{
String name;
int age;
public Student() {
}
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 +
'}';
}
}
2.List
2.1 List集合概述
List集合概述:
- 有序集合(也称为序列),用户可以精确控制每个元素插入的位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
List集合特点:
- 有序:存储和取出的元素顺序一致
- 可重复:存储的元素可以重复
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//List集合概述
public class ListDemo01 {
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<String>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
list.add("hello");
//输出集合元素
System.out.println(list);//[hello, world, java, hello]
//迭代器方式遍历集合
Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
2.2 List集合特有方法
方法名 | 说明 |
---|---|
void add(int index,E element) | 在此集合中指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
package com.advanced.collection;
import java.util.ArrayList;
import java.util.List;
//List集合特有方法
public class ListDemo02 {
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<String>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
//输出集合元素
System.out.println(list);//[hello, world, java]
System.out.println("========================");
//void add(int index,E element):在此集合中指定位置插入指定的元素
// list.add(4,"javase");//IndexOutOfBoundsException
list.add(0,"javase");
System.out.println(list);//[javase, hello, world, java]
System.out.println("========================");
//E remove(int index):删除指定索引处的元素,返回被删除的元素
String s1 = list.remove(0);
System.out.println(s1);//javese
System.out.println(list);//[hello, world, java]
System.out.println("========================");
//E set(int index,E element):修改指定索引处的元素,返回被修改的元素
String s2 = list.set(2, "javase");
System.out.println(s2);//java
System.out.println(list);//[hello, world, javase]
System.out.println("========================");
//E get(int index):返回指定索引处的元素
String s3 = list.get(2);
System.out.println(s3);//javase
System.out.println("========================");
//遍历集合
System.out.println(list.get(0));//hello
System.out.println(list.get(1));//world
System.out.println(list.get(2));//javase
System.out.println("========================");
//用for循环改进遍历
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
}
}
2.3 案例
List集合存储学生对象并遍历
需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//案例:List集合存储学生对象并遍历
public class ListDemo03 {
public static void main(String[] args) {
//创建集合对象
List<Student> list = new ArrayList<Student>();
//创建学生对象
Student s1 = new Student("罗沙雕", 20);
Student s2 = new Student("陈大锤", 25);
Student s3 = new Student("苏斩妹", 28);
//添加学生对象到集合
list.add(s1);
list.add(s2);
list.add(s3);
//方式1:使用迭代器方式遍历
Iterator<Student> it = list.iterator();
while(it.hasNext()){
Student student = it.next();
System.out.println(student);
}
//方式2:使用for循环方式遍历
for (int i = 0; i < list.size(); i++) {
Student student = list.get(i);
System.out.println(student);
}
}
}
2.4 并发修改异常
并发修改异常:ConcurrentModificationException
产生原因:迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断实际修改值与预期修改值不一致
解决方法:用for循环遍历,然后集合对象做相应的操作即可
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//ConcurrentModificationException:当不允许这样的修改时,可以通过检测到对象的并发修改方法来抛出此异常
public class ListDemo04 {
public static void main(String[] args) {
//创建List集合对象
List<String> list = new ArrayList<String>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
//遍历:如果集合中有元素"world",则添加一个元素"javase"
/*
Iterator<String> it = list.iterator();//多态方式创建迭代器对象
while (it.hasNext()){
String s = it.next();//ConcurrentModificationException:并发修改异常
if (s.equals("world")){
list.add("javase");
}
}
*/
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);//get()方法未做实际修改值与预期修改值判断
if (s.equals("world")){
list.add("javase");
}
}
System.out.println(list);
}
}
并发修改异常源码分析
//源代码截取内容
public interface List<E> extends Collection<E> {
boolean add(E e);
E get(int index);
Iterator<E> iterator();
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int expectedModCount = modCount;
/*
modCount:实际修改集合的次数
expectedModCount:预期修改集合的次数
*/
public E next() {//需要判断比较实际修改值与预期修改值是否相等
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)//这里判断实际修改值与预期修改值导致抛出异常
throw new ConcurrentModificationException();
}
}
public boolean add(E e) {//add()方法添加元素后modCount++
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
}
public E get(int index) {//不需要判断比较实际修改值与预期修改值是否相等
rangeCheck(index);
return elementData(index);
}
}
2.5 ListIterator
ListIterator:列表迭代器
- 通过List集合的ListIterator方法得到,所以说它是List集合特有的迭代器
- 允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中的迭代器的当前位置
ListIterator中的常用方法
- E next():返回迭代中的下一个元素
- boolean hasNext():如果迭代器中具有更多元素,则返回true
- E previous():返回列表中的上一个元素
- boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
- void add(E e):在列表中插入指定的元素
package com.advanced.collection;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
//ListIterator列表迭代器
public class ListIteratorDemo01 {
public static void main(String[] args) {
//创建列表对象
List<String> list = new ArrayList<String>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
//创建ListIterator迭代器对象
ListIterator<String> st = list.listIterator();
//E next():返回迭代中的下一个元素
String s = st.next();
String s2 = st.next();
System.out.println(s);//hello
//boolean hasNext():如果迭代器中具有更多元素,则返回true
System.out.println(st.hasNext());//true
//E previous():返回列表中的上一个元素
String previous = st.previous();
System.out.println(previous);//world
//boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
System.out.println(st.hasPrevious());//true
System.out.println("============================");
//1.next方法向后遍历
while (st.hasNext()) {
String s1 = st.next();
System.out.println(s1);
}
System.out.println("============================");
//2.previous方法向前遍历
while (st.hasPrevious()) {
String p1 = st.previous();
System.out.println(p1);
}
System.out.println("============================");
//void add(E e):在列表中插入指定的元素
st.add("javase");
while (st.hasPrevious()) {
String previous1 = st.previous();
System.out.println(previous1);
}
while(st.hasNext()){
String s1 = st.next();
if (s1.equals("world")){
st.add("javaee");//使用列表迭代器add方法,不会报并发修改异常
}
}
}
}
不会并发修改异常源码分析
//源码分析
public interface List<E> extends Collection<E> {
boolean add(E e);
ListIterator<E> listIterator();
}
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
void add(E e);
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
public ListIterator<E> listIterator() {
return new ListItr(0);
}
private class ListItr extends Itr implements ListIterator<E> {
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount; //列表迭代器add方法将实际修改值赋值给预期修改值
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
private class Itr implements Iterator<E> {
int expectedModCount = modCount;//开始时,实际修改值与预期修改值一致
/*
modCount:实际修改集合的次数
expectedModCount:预期修改集合的次数
*/
public E next() {//需要判断比较实际修改值与预期修改值是否相等
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)//这里判断实际修改值与预期修改值导致抛出异常
throw new ConcurrentModificationException();
}
}
}
modCount:实际修改集合的次数
expectedModCount:预期修改集合的次数
开始时,实际修改值与预期修改值一致,expectedModCount = modCount,List集合对象调用add()方法后,modCount++,此时modCount != expectedModCount,Iterator迭代器对象调用next()方法,抛出ConcurrentModificationException;而列表迭代器对象调用add()方法后,expectedModCount = modCount,因此再调用next()方法,实际修改值与预期修改值一致,不会报并发修改异常
2.6 增强for循环
增强for:简化数组和Collection集合的遍历
- 实现Iterable接口的类允许其对象称为增强for语句的目标
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
增强for格式:
for(元素数据类型 变量名:数组或Collection集合){
//在此处使用变量即可,该变量就是元素
}
package com.advanced.collection;
import java.util.ArrayList;
import java.util.List;
//增强for
public class LsitForeachDemo {
public static void main(String[] args) {
//创建List集合对象
List<String> list = new ArrayList<String>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
//增强for遍历列表元素
for (String s:list) {
System.out.println(s);
}
System.out.println("==================");
//内部原理:一个Iterator迭代器
/*
for (String s:list) {
if (s.equals("world"))
list.add("javase");//ConcurrentModificationException
}
*/
}
}
2.7 案例
List集合存储学生对象用三种方式遍历
需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
//案例:List集合存储学生对象用三种方式遍历
public class ListDemo05 {
public static void main(String[] args) {
//创建List列表对象
List<Student> list = new ArrayList<Student>();
//创建学生对象
Student s1 = new Student("上清天尊", 20);
Student s2 = new Student("太清天尊", 23);
Student s3 = new Student("玉清天尊", 18);
//添加学生对象到列表
list.add(s1);
list.add(s2);
list.add(s3);
//创建Iterator迭代器对象
Iterator<Student> it = list.iterator();
//创建ListIterator迭代器对象
ListIterator<Student> lit = list.listIterator();
//方式一:普通for方式遍历
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
System.out.println("================================");
//方式二:Iterator迭代器遍历
while (it.hasNext()){
Student s = it.next();
System.out.println(s);
}
System.out.println("================================");
//方式三:增强for遍历
for (Student s:list) {
System.out.println(s);
}
}
}
2.8 常见数据结构之栈
数据进入栈模型的过程称为:压/进栈
数据离开栈模型的过程称为:弹/出栈
栈是一种数据先进后出的模型
2.9 常见数据结构之队列
数据从后端进入队列模型的过程称为:入队列
数据从前端离开队列模型的过程称为:出队列
队列是一种数据先进先出的模型
2.10 常见数据结构之数组
查询数据通过索引定位,查询任意数据耗时相同,查询效率高
删除数据时,需要将原始数据删除,并将后面数据每个前移,删除效率低
添加数据时,需要将添加数据位置后数据后移,再添加元素,添加效率低
数组是一种查询快,增删慢的模型
2.11 常见数据结构之链表
链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表是一种查询慢,增删快的模型(对比数组)
2.12 List集合子类特点
List集合常用子类:ArrayList,LinkedList
- ArrayList:底层数据结构是数组,查询快,增删慢
- LinkedList:底层数据结构是链表,查询慢,增删快
练习:分别使用ArrayList和LinkedList存储字符串数据并遍历
-
ArrayList存储字符串的三种遍历方式
package com.advanced.collection; import java.util.ArrayList; import java.util.Iterator; //练习:分别使用ArrayList和LinkedList存储字符串数据并遍历 public class ListDemo06 { public static void main(String[] args) { //创建List列表对象 ArrayList<String> list = new ArrayList<String>(); //添加字符串元素 list.add("hello"); list.add("world"); list.add("java"); //迭代器方式遍历 Iterator<String> it = list.iterator(); while (it.hasNext()){ String s = it.next(); System.out.println(s); } System.out.println("=========="); //普通for遍历 for (int i = 0; i < list.size(); i++) { String s = list.get(i); System.out.println(s); } System.out.println("=========="); //增强for遍历 for(String s:list){ System.out.println(s); } } }
-
LinkedList存储字符串的三种遍历方式
package com.advanced.collection; import java.util.Iterator; import java.util.LinkedList; //练习:分别使用ArrayList和LinkedList存储字符串数据并遍历 public class ListDemo07 { public static void main(String[] args) { //创建List列表对象 LinkedList<String> list = new LinkedList<String>(); //添加字符串元素 list.add("hello"); list.add("world"); list.add("java"); //普通for遍历 for (int i = 0; i < list.size(); i++) { String s = list.get(i); System.out.println(s); } System.out.println("=========="); //增强for遍历 for(String s : list){ System.out.println(s); } System.out.println("=========="); //迭代器方式遍历 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ String s = iterator.next(); System.out.println(s); } } }
2.13 案例
ArrayList集合存储学生对象用三种方式遍历
需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Iterator;
//ArrayList集合存储学生对象用三种方式遍历
public class ListDemo08 {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> list = new ArrayList<>();
//创建学生对象
Student s1 = new Student("Tom", 20);
Student s2 = new Student("Sam", 25);
Student s3 = new Student("Jaker", 23);
//添加元素
list.add(s1);
list.add(s2);
list.add(s3);
//方式1:迭代器方式遍历
Iterator<Student> it = list.iterator();
while (it.hasNext()){
Student s = it.next();
System.out.println(s);
}
System.out.println("==================");
//方式2:普通for遍历
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
System.out.println("==================");
//方式3:增强for遍历
for(Student s:list){
System.out.println(s);
}
}
}
LinkedList集合存储学生对象用三种方式遍历
具体实现代码与上面类似
2.14 LinkedList特有方法
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定元素 |
public void addLast(E e) | 将指定元素追加到此列表末尾 |
public E getFirst() | 返回此列表第一个元素 |
public E getLast() | 返回此列表最后一个元素 |
public E removeFirst() | 从此列表删除并返回第一个元素 |
public E removeLast() | 从此列表删除并返回最后一个元素 |
package com.advanced.collection;
import java.util.LinkedList;
//LinkedList特有方法
public class ListDemo09 {
public static void main(String[] args) {
//创建集合对象
LinkedList<String> list = new LinkedList<>();
//添加元素
list.add("hello");
list.add("world");
list.add("java");
System.out.println(list);//[hello, world, java]
System.out.println("=====================================");
//public void addFirst(E e)在该列表开头插入指定元素
list.addFirst("javase");
System.out.println(list);//[javase, hello, world, java]
System.out.println("=====================================");
//public void addLast(E e)将指定元素追加到此列表末尾
list.addLast("javaee");
System.out.println(list);//[javase, hello, world, java, javaee]
System.out.println("=====================================");
//public E getFirst()返回此列表第一个元素
String first = list.getFirst();
System.out.println(first);//javase
System.out.println("=====================================");
//public E getLast()返回此列表最后一个元素
String last = list.getLast();
System.out.println(last);//javaee
System.out.println("=====================================");
//public E removeFirst()从此列表删除并返回第一个元素
String removeFirst = list.removeFirst();
System.out.println(removeFirst);//javase
System.out.println(list);//[hello, world, java, javaee]
System.out.println("=====================================");
//public E removeLast()从此列表删除并返回最后一个元素
String removeLast = list.removeLast();
System.out.println(removeLast);//javaee
System.out.println(list);//[hello, world, java]
}
}
3.Set
3.1 Set集合概述
Set集合特点:
- 不包含重复元素的集合
- 没有带索引的方法,所以不能使用普通for循环遍历
Set集合练习:存储字符串并遍历
package com.advanced.collection;
import java.util.HashSet;
import java.util.Set;
//Set集合
//HashSet:对集合的迭代顺序不做任何保证
public class SetDemo01 {
public static void main(String[] args) {
//创建集合对象
Set<String> set = new HashSet<String>();
//添加元素
set.add("hello");
set.add("world");
set.add("java");
//不包含重复元素
set.add("world");
//遍历元素
for(String s:set){
System.out.println(s);
}
}
}
3.2 哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
public int hashCode():返回对象的哈希值
哈希值特点
- 同一对象多次调用hashCode()方法返回的哈希值相同
- 默认情况下,不同对象调用hashCode()方法返回的哈希值不同。而重写hashCode()方法,可以实现不同对象的哈希值相同
package com.advanced.collection;
//哈希值
public class SetDemo02 {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("Tom", 20);
Student s2 = new Student("Sam", 23);
//同一对象多次调用hashCode()方法返回哈希值相同
System.out.println(s1.hashCode());//460141958
System.out.println(s1.hashCode());//460141958
//默认情况下,不同对象的哈希值是不同的
//通过方法重写,可以实现不同对象的哈希值相同
System.out.println(s2.hashCode());//1163157884
System.out.println("==============================");
System.out.println("hello".hashCode());//99162322
System.out.println("world".hashCode());//113318802
System.out.println("java".hashCode());//3254818
System.out.println("world".hashCode());//113318802
System.out.println("==============================");
System.out.println("北京".hashCode());//679541
System.out.println("成都".hashCode());//815341
System.out.println("重地".hashCode());//1179395
System.out.println("通话".hashCode());//1179395
}
}
3.3 HashSet集合概述
-
HashSet集合特点:
- 底层数据结构是哈希表
- 对集合的迭代顺序不做任何保证,也就是说不保证存储和取出元素顺序一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
-
HashSet集合练习:
- 存储字符串并遍历
package com.advanced.collection;
import java.util.HashSet;
//HashSet集合:存储字符串并遍历
public class HashSetDemo01 {
public static void main(String[] args) {
//创建集合对象
HashSet<String> hs = new HashSet<String>();
//添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
//遍历元素
for(String s: hs){
System.out.println(s);
}
}
}
3.4 HashSet元素唯一性
HashSet集合存储元素:要保证元素的唯一性,需要重写hashCode()和equals()方法
HashSet添加一个元素的过程:
1.调用对象的hashCode()方法获取对象哈希值
2.根据调用对象的哈希值计算对象的存储位置
3.判断该位置是否有元素存在
①如果没有,则将元素存储到该位置;
②如果有,遍历该位置的所有元素,和新存入的元素比较哈希值是否相同,
Ⅰ. 如果都不相同,则将元素存储到该位置
Ⅱ. 如果有相同的,调用equals()方法比较对象内容是否相同
i)如果不同,将元素存储到该位置
ii)如果相同,说明元素重复,不存储
源码分析
//HashSet集合保证元素唯一性源码分析
hs.add("world");
//================================================================
public class HashSet<E> extends AbstractSet<E> implements Set<E>{
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
}
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>{
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//hash值和元素的hashCode()方法有关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果哈希表未初始化,就对它初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//根据对象的哈希值计算对象的存储位置,如果该位置没有元素,就存储该元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
/*
存入的元素和以前的元素比较哈希值
如果哈希值不同,会继续向下执行,把元素添加到集合
如果哈希值相同,则会调用对象的equals()方法比较
如果返回false,会继续向下执行,把元素添加到集合
如果返回true,说明元素重复,不存储
*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
}
}
}
3.5 常见数据结构之哈希表
哈希表:
- JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
- JDK8之后,在长度比较长的时候,底层实现了优化
3.6 案例
HashSet集合存储学生对象并遍历
需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
需求:学生对象的成员变量值相同,我们就认为是同一个对象
注意:学生类需要重写hashCode()和equals(),自动生成即可
package com.advanced.collection;
import java.util.HashSet;
//HashSet集合存储学生对象并遍历
//需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合
//需求:学生对象的成员变量值相同,我们就认为是同一个对象
public class HashSetDemo02 {
public static void main(String[] args) {
//创建集合对象
HashSet<Student> hs = new HashSet<Student>();
//创建学生对象
Student s1 = new Student("Tom", 5);
Student s2 = new Student("Jerry", 3);
Student s3 = new Student("Sam", 25);
Student s4 = new Student("Tom", 5);
//添加元素
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
//遍历元素
for(Student s:hs){
System.out.println(s);
}
}
}
/*
输出结果:
Student{name='Sam', age=25}
Student{name='Jerry', age=3}
Student{name='Tom', age=5}
Student{name='Tom', age=5}
重复元素依然输出,要使内容相同为同一个对象,需要在学生类重写hashCode()和equals()
*/
3.7 LinkedHashSet集合
LinkedHashSet集合特点
- 哈希表和链表实现的Set接口,具有可预测的迭代次数
- 由链表保证数据有序,也就是说元素存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说元素没有重复的
LinkedHashSet集合练习
- 存储字符串并遍历
package com.advanced.collection;
import java.util.LinkedHashSet;
//LinkedHashSet集合
public class LinkedHashSetDemo01 {
public static void main(String[] args) {
//创建集合对象
LinkedHashSet<String> lsh = new LinkedHashSet<>();
//添加字符串元素
lsh.add("hello");
lsh.add("world");
lsh.add("java");
lsh.add("java");
//遍历元素
for(String s: lsh){
System.out.println(s);
}
}
}
/*
输出结果:
hello
world
java
*/
3.8 TreeSet集合
TreeSet集合特点
- 元素有序,这里的顺序不是指元素存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
TreeSet集合练习
- 存储整数并遍历
package com.advanced.collection;
import java.util.TreeSet;
//TreeSet集合练习:存储整数并遍历
public class TreeSetDemo01 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Integer> tr = new TreeSet<Integer>();
//添加元素
tr.add(2);
tr.add(5);
tr.add(4);
tr.add(1);
tr.add(3);
//遍历元素
for (Integer i : tr) {
System.out.println(i);
}
}
}
/*
1
2
3
4
5
*/
3.9 Comparable使用
存储学生对象并遍历,创建TreeSet集合使用无参构造方法>
要求:按照年龄从小到大排序,年龄相同则按姓名字母顺序排序
结论:
- 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
package com.advanced.collection;
import java.util.TreeSet;
//Comparable
public class TreeSetComparable01 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
//创建学生对象
Student s1 = new Student("zhangsan", 20);
Student s2 = new Student("lisi", 25);
Student s3 = new Student("wangwu", 23);
Student s4 = new Student("zhaoliu", 25);
Student s5 = new Student("zhaoliu", 25);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历元素
for(Student s: ts){
System.out.println(s);
}
}
}
class Student implements Comparable<Student> {
//其余属性方法省略
@Override
public int compareTo(Student s) {
//return 0;//表示两个对象相同
//return 1;//表示后一个大,按添加顺序排序
//return -1;//表示前一个大,按添加顺序倒序排序
//按年龄从小到大排序
int num = this.age - s.age;
//年龄相同,按照名字字母顺序排序
int n = num == 0 ? this.name.compareTo(s.name) : num;
return n;
}
}
/*
Student{name='zhangsan', age=20}
Student{name='wangwu', age=23}
Student{name='lisi', age=25}
Student{name='zhaoliu', age=25}
*/
3.10 Comparator比较器
存储学生对象并遍历,创建TreeSet集合使用带参构造方法>
要求:按照年龄从小到大排序,年龄相同则按姓名字母顺序排序
结论:
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
package com.advanced.collection;
import java.util.Comparator;
import java.util.TreeSet;
//Comparator比较器
public class TreeSetComparator01 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// int num = this.age - s.age;
// s1 s2
int num1 = s1.getAge() - s2.getAge();
int num2 = num1==0?s1.getName().compareTo(s2.getName()):num1;
return num2;
}
});
//创建学生对象
Student s1 = new Student("zhangsan", 20);
Student s2 = new Student("lisi", 25);
Student s3 = new Student("wangwu", 23);
Student s4 = new Student("zhaoliu", 25);
Student s5 = new Student("zhaoliu", 25);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历元素
for(Student s: ts){
System.out.println(s);
}
}
}
3.11 案例:成绩排序
需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
需求:按照总分从高到低出现
package com.advanced.collection;
import java.util.Comparator;
import java.util.TreeSet;
//案例:成绩排序(Comparator比较器)
public class TreeSetDemo02 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int score1 = s1.getChineseScore() + s1.getMathScore();
int score2 = s2.getChineseScore() + s2.getMathScore();
//主要条件
int num1 = score2 - score1;
//次要条件
int num2 = num1 == 0 ? s1.getChineseScore() - s2.getChineseScore() : num1;
int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
return num3;
}
});
//创建学生对象
Student s1 = new Student("张雅", 90, 86);
Student s2 = new Student("姬玲珑", 98, 88);
Student s3 = new Student("白灵", 89, 89);
Student s4 = new Student("紫月", 91, 85);
Student s5 = new Student("黎若雨", 85, 90);
Student s6 = new Student("张雅", 90, 86);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历元素
for (Student s : ts) {
int socre = s.getChineseScore() + s.getMathScore();
System.out.println(s.getName() + ", 语文成绩:" + s.getChineseScore()
+ ", 数学成绩:" + s.getMathScore() + ", 总分:" + socre);
}
}
}
/*
姬玲珑, 语文成绩:98, 数学成绩:88, 总分:186
白灵, 语文成绩:89, 数学成绩:89, 总分:178
张雅, 语文成绩:90, 数学成绩:86, 总分:176
紫月, 语文成绩:91, 数学成绩:85, 总分:176
黎若雨, 语文成绩:85, 数学成绩:90, 总分:175
*/
3.12 案例:不重复的随机数
需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
package com.advanced.collection;
import java.util.HashSet;
import java.util.Random;
//案例:不重复的随机数
//需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
public class HashSetDemo03 {
public static void main(String[] args) {
//创建集合对象
HashSet<Integer> hs = new HashSet<Integer>();
//创建随机数对象
Random random = new Random();
//循环添加随机数元素
while (hs.size()<10){
int i = random.nextInt(20)+1;
hs.add(i);
}
for(Integer i : hs){
System.out.println(i);
}
}
}
同理,可以使用TreeSet集合。HashSet集合元素无序,TreeSet集合元素自然排序
4.泛型
4.1 泛型概述
泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法类型
它的本质是参数化类型,也就是说所操作的类型被定义为一个参数
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参,那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来具体的类型参数化,然后在使用/调用时传入具体的类型
这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法和泛型接口
泛型定义格式:
- <类型>:指定一种类型的格式。这里的类型可以看成是形参
- <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
- 将来具体调用时给定的类型可以看成实参,并且实参的类型只能是引用数据类型
泛型的好处:
- 把运行时期的问题提前到编译期间
- 避免了强制类型转换
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
//泛型
public class CollectionDemo04 {
public static void main(String[] args) {
//创建集合对象
//Collection c = new ArrayList();
Collection<String> c = new ArrayList<String>();
//添加对象
c.add("hello");
c.add("world");
c.add("java");
//c.add(20);//定义泛型后只能传递指定类型
for (Object o:c){
//String s = (String) o;//
System.out.println(o);
}
}
}
4.2 泛型类
泛型类定义格式:
格式:修饰符 class 类名<类型>{}
范例:public class Generic< T>{}
此处T可以随便写为任意标识。常见的如T、E、K、V等形式的参数常用于表示泛型
package com.advanced.collection;
import com.oop.demo05.Teacher;
//泛型类
public class GenericDemo02 {
public static void main(String[] args) {
//创建学生类对象
Student s = new Student();
s.setName("张伟");
System.out.println(s.getName());//张伟
//创建教师类对象
Teacher t = new Teacher();
t.setAge(30);
System.out.println(t.getAge());//30
System.out.println("==========");
//创建泛型类对象
Generic<String> sg = new Generic<String>();
sg.setT("张伟");
System.out.println(sg.getT());//张伟
Generic<Integer> ig = new Generic<>();
ig.setT(30);
System.out.println(ig.getT());//30
Generic<Boolean> bg = new Generic<>();
bg.setT(true);
System.out.println(bg.getT());//true
}
}
class Generic<T>{
T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
4.3 泛型方法
泛型方法定义格式:
格式:修饰符 <类型> 返回值类型 方法名(类型 变量名){}
package com.advanced.collection;
//泛型方法
public class GenericDemo03 {
public static void main(String[] args) {
//创建对象
GenericDemo03 gd = new GenericDemo03();
//调用泛型方法
gd.show("hello");//hello
gd.show(20);//20
gd.show(true);//true
}
//泛型方法
public <T> void show(T t){
System.out.println(t);
}
}
4.4 泛型接口
泛型接口定义格式:
格式:修饰符 interface 接口名<类型>{}
package com.advanced.collection;
//泛型接口测试类
public class GenericDemo04 {
public static void main(String[] args) {
//创建对象
Generic1<String> g1= new GenericImpl<String>();
g1.show("林青霞");
Generic1<Integer> g2= new GenericImpl<Integer>();
g2.show(30);
}
}
//泛型接口
interface Generic1<T>{
public void show(T t);
}
//泛型接口实现类
class GenericImpl<T> implements Generic1<T>{
@Override
public void show(T t) {
System.out.println(t);
}
}
4.5 类型通配符
为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符:<?>
- List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
- 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以通过类型通配符的上限
- 类型通配符上限:<? extends 类型>
- List<? extends Number>:他表示的类型是Number或其子类型
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限
- 类型通配符下限:<? super 类型>
- List<? super Number>:它表示的类型是Number或其父类型
package com.advanced.collection;
import java.util.ArrayList;
import java.util.List;
//类型通配符
public class GenericDemo05 {
public static void main(String[] args) {
//List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
List<?> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<Number>();
List<?> list3 = new ArrayList<Integer>();
//List<? extends Number>:他表示的类型是Number或其子类型
// List<? extends Number> list4 = new ArrayList<Object>();// Object -> Number -> Integer
List<? extends Number> list5 = new ArrayList<Number>();
List<? extends Number> list6 = new ArrayList<Integer>();
//List<? super Number>:它表示的类型是Number或其父类型
List<? super Number> list7 = new ArrayList<Object>();
List<? super Number> list8 = new ArrayList<Number>();
// List<? super Number> list9 = new ArrayList<Integer>();// Object -> Number -> Integer
}
}
4.6 可变参数
可变参数又称为参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
- 格式:修饰符 返回值类型 方法名(数据类型… 变量名){}
- 范例:public static int sum(int… a){}
可变参数注意事项:
- 这里的变量其实是一个数组
- 如果一个方法有多个变量,包含可变参数,
可变参数要放在最后面
package com.advanced.collection;
//可变参数
public class ArgsDemo01 {
public static void main(String[] args) {
System.out.println(sum(10, 20));
System.out.println(sum(10, 20, 30));
System.out.println(sum(10, 20, 30, 40));//如果多个参数使用方法重载很麻烦,推荐使用可变参数
System.out.println(sum(10, 20, 30, 40, 50));
System.out.println(sum(10, 20, 30, 40, 50, 60));
}
// public static int sum(int b,int... a){return 0;}//可变参数放最后
public static int sum(int...a){
// System.out.println(a);//[I@1b6d3586 从输出结果可以看出a是一个数组
int sum = 0;
for (int i:a){
sum += i;
}
return sum;
}
public static int sum(int a,int b){
return a+b;
}
public static int sum(int a,int b,int c){
return a+b+c;
}
}
4.7 可变参数的使用
Arrays工具类中有一个静态方法::
public static List asList(T…a):返回由指定数组支持的固定大小的列表
返回的集合不能做增删操作,可以做修改操作
List接口中有一个静态方法:
public static List of(E… elements):返回包含任意数量元素的不可变列表
返回的集合不能做增删改操作
Set接口中有一个静态方法:
public static Set of(E… elements):返回一个包含任意数量元素的不可变集合
在给元素的时候,不能给重复的元素
返回的集合不能做增删操作,没有修改方法
package com.advanced.collection;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
//可变参数的使用
public class ArgsDemo02 {
public static void main(String[] args) {
//public static <T> List<T> asList(T...a):返回由指定数组支持的固定大小的列表
/*
List<String> list1 = Arrays.asList("hello", "world", "java");
System.out.println(list1);//[hello, world, java]
// list1.add("javase");//UnsupportedOperationException
// list1.remove("world");//UnsupportedOperationException
list1.set(1,"javase");
System.out.println(list1);//[hello, javase, java]
*/
//public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表
/*
// List<String> list2 = List.of("hello","world","java");
// list2.add("javase");//UnsupportedOperationException
// list2.remove("world");//UnsupportedOperationException
// list2.set(1,"javase");
*/
//public static <E> Set<E> of(E... elements):返回一个包含任意数量元素的不可变集合
/*
// Set<String> list3 = Set.of("hello","world","java");
// Set<String> list3 = Set.of("hello","world","java","world");//IllegalArgumentException
// list3.add("javase");//UnsupportedOperationException
// list3.remove("world");//UnsupportedOperationException
*/
}
}
5.Map
5.1 Map集合概述
Map集合概述
interface Map<K,V> K:键的类型 V:值的类型
将键映射到值的类型;不能包含重复的键,每个键可以映射到最多一个值
举例:学生的学号和姓名 (学号是键,姓名是值)
001 林青霞
002 张曼玉
003 王祖贤
创建Map集合对象
- 多态的方法
- 具体的实现类HashMap
package com.advanced.collection;
import java.util.HashMap;
import java.util.Map;
//Map集合
public class MapDemo01 {
public static void main(String[] args) {
//创建Map集合对象
Map<String ,String> map1 = new HashMap<String, String>();
//添加元素
map1.put("001","林青霞");
map1.put("002","张曼玉");
map1.put("003","王祖贤");
//输出集合对象
System.out.println(map1);//{001=林青霞, 002=张曼玉, 003=王祖贤}
map1.put("003","柳岩");//该键存在元素,则会覆盖以前元素
System.out.println(map1);//{001=林青霞, 002=张曼玉, 003=柳岩}
}
}
5.2 Map集合基本功能
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 删除所有键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,集合中键值对的个数 |
package com.advanced.collection;
import java.util.HashMap;
import java.util.Map;
//Map集合基本功能
public class MapDemo02 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> hm = new HashMap<String, String>();
//V put(K key,V value) 添加元素
hm.put("001","林青霞");
hm.put("002","张曼玉");
//输出元素
System.out.println(hm);//{001=林青霞, 002=张曼玉}
//int size() 集合的长度,集合中键值对的个数
int size = hm.size();
System.out.println(size);//2
//V remove(Object key) 根据键删除键值对元素
hm.remove("001");
System.out.println(hm);//{002=张曼玉}
//boolean containsKey(Object key) 判断集合是否包含指定的键
System.out.println(hm.containsKey("001"));//false
System.out.println(hm.containsKey("002"));//true
//boolean containsValue(Object value) 判断集合是否包含指定的值
System.out.println(hm.containsValue("张曼玉"));//true
System.out.println(hm.containsValue("林青霞"));//false
//boolean isEmpty() 判断集合是否为空
System.out.println(hm.isEmpty());//false
//void clear()删除所有键值对元素
hm.clear();
System.out.println(hm);//{}
}
}
5.3 Map集合获取功能
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Collection values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 所有键值对对象的集合 |
package com.advanced.collection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//Map集合获取功能
public class MapDemo03 {
public static void main(String[] args) {
//创建集合对象
Map<String,String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌","赵敏");
map.put("郭靖","黄蓉");
map.put("杨过","小龙女");
//V get(Object key) 根据键获取值
System.out.println(map.get("张无忌"));//赵敏
System.out.println(map.get("张三丰"));//null
//Set<K> keySet() 获取所有键的集合
Set<String> keySet = map.keySet();
System.out.println(keySet);//[杨过, 郭靖, 张无忌]
//Collection<V> values() 获取所有值的集合
Collection<String> values = map.values();
System.out.println(values);//[小龙女, 黄蓉, 赵敏]
}
}
5.4 Map集合遍历(一)
我们刚才存储元素都是成对出现的,所以我们可以把Map看成是一个夫妻对集合
遍历思路:
-
把所有丈夫集中起来
-
遍历丈夫集合,获取每一个丈夫
-
根据丈夫去找对应的妻子
转换为Map集合中的操作:
- 获取所有键的集合,用keySet()方法实现
- 遍历键的集合,获取到每一个键,用增强for实现
- 根据键去找值,用get(Oject key)方法实现
package com.advanced.collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//Map集合遍历(方式1)
public class MapDemo04 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//遍历元素(方式1)
Set<String> keySet = map.keySet();//获取所有键的集合
for (String s : keySet) {//遍历键的集合
String s1 = map.get(s);//通过键获取值
System.out.println(s + "," + s1);
}
}
}
5.5 Map集合遍历(二)
我们刚才存储的元素都是成对出现的,所以我们可以把Map看成是一个夫妻对的集合
遍历思路:
-
获取所有结婚证的集合
-
遍历结婚证的集合,得到每一个结婚证
-
根据结婚证获取丈夫和妻子
转换Map集合中的操作:
-
获取所有键值对对象
Set<Map.Entry<K,V>> entrySet():获取所有的键值对对象的集合
-
遍历键值对对象的集合,得到每一个键值对
用增强for实现,得到每一个Map.Entry
-
根据键值对对象获取键和值
用getKey()得到键
用getValue()得到值
package com.advanced.collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Map集合遍历(方式2)
public class MapDemo05 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//遍历元素(方式2)
Set<Map.Entry<String, String>> entrySet = map.entrySet();//获取键值对对象
for (Map.Entry<String,String> entry : entrySet) {//遍历键值对对象的集合
String key = entry.getKey();//通过对象获取键
String value = entry.getValue();//通过对象获取值
System.out.println(key + "," + value);
}
}
}
5.6 案例
HashMap集合存储学生对象并遍历
需求:创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历
package com.advanced.collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//HashMap集合存储学生对象并遍历
//需求:创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历
public class HashMapDemo01 {
public static void main(String[] args) {
//创建集合对象
HashMap<String, Student> hm = new HashMap<String, Student>();
//创建学生对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("张曼玉", 35);
Student s3 = new Student("王祖贤", 33);
//添加元素
hm.put("001", s1);
hm.put("002", s2);
hm.put("003", s3);
//遍历元素(方式1)
/*
Set<String> keySet = hm.keySet();
for (String s : keySet) {
Student student = hm.get(s);
System.out.println(s + "," + student.getName() + "," + student.getAge());
}
*/
//遍历元素(方式2)
Set<Map.Entry<String, Student>> entrySet = hm.entrySet();
for (Map.Entry<String, Student> entry : entrySet) {
String key = entry.getKey();
Student value = entry.getValue();
String name = value.getName();
int age = value.getAge();
System.out.println(key + "," + name + "," + age);
}
}
}
5.7 案例
HashMap集合存储学生对象并遍历
需求:创建一个HashMap集合,键是学生对象(Student),值是居住地(String)。存储多个键值对元素,并遍历
要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
package com.advanced.collection;
import java.util.HashMap;
import java.util.Set;
//HashMap集合存储学生对象并遍历
//需求:创建一个HashMap集合,键是学生对象(Student),值是居住地(String)。存储多个键值对元素,并遍历
//要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
public class HashMapDemo02 {
public static void main(String[] args) {
//创建集合对象
HashMap<Student, String> hm = new HashMap<Student,String>();
//创建学生对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("张曼玉", 35);
Student s3 = new Student("王祖贤", 33);
Student s4 = new Student("王祖贤", 32);
//添加元素
hm.put(s1,"北京");
hm.put(s2,"上海");
hm.put(s3,"香港");
hm.put(s3,"成都");
hm.put(s4,"深圳");
//遍历元素
Set<Student> keySet = hm.keySet();
for (Student s : keySet){
System.out.println(s.getName()+","+s.getAge()+","+hm.get(s));
}
}
}
输出结果:
王祖贤,33,成都
张曼玉,35,上海
王祖贤,32,深圳
林青霞,30,北京
5.8 案例
ArrayList集合存储HashMap元素并遍历
需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历
package com.advanced.collection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
//ArrayList集合存储HashMap元素并遍历
//需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历
public class HashMapDemo03 {
public static void main(String[] args) {
//创建ArrayList集合对象
ArrayList<HashMap<String, String>> arrayList = new ArrayList<HashMap<String, String>>();
//创建HashMap集合对象
HashMap<String, String> hashMap1 = new HashMap<>();
HashMap<String, String> hashMap2 = new HashMap<>();
HashMap<String, String> hashMap3 = new HashMap<>();
//添加HashMap集合元素
hashMap1.put("孙策", "大乔");
hashMap1.put("周瑜", "小乔");
hashMap2.put("郭靖", "黄蓉");
hashMap2.put("杨过", "小龙女");
hashMap3.put("令狐冲", "任盈盈");
hashMap3.put("林平之", "岳灵珊");
//添加元素
arrayList.add(hashMap1);
arrayList.add(hashMap2);
arrayList.add(hashMap3);
//遍历元素(嵌套循环遍历)
for (HashMap<String, String> hashMap : arrayList) {//外层遍历ArrayList集合
Set<String> keySet = hashMap.keySet();
for (String key : keySet) { //内层遍历HashMap集合
String value = hashMap.get(key);
System.out.println(key + "," + value);
}
}
}
}
输出结果:
孙策,大乔
周瑜,小乔
杨过,小龙女
郭靖,黄蓉
令狐冲,任盈盈
林平之,岳灵珊
5.9 案例
HashMap集合存储ArrayList元素并遍历
需求:创建一个HashMap集合,存储三个键值对元素,每个键值对元素键是String,值是ArrayList,每个ArrayList元素是String,并遍历
package com.advanced.collection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
//HashMap集合存储ArrayList元素并遍历
//需求:创建一个HashMap集合,存储三个键值对元素,每个键值对元素键是String,值是ArrayList,每个ArrayList元素是String,并遍历
public class HashMapDemo04 {
public static void main(String[] args) {
//创建HashMap集合对象
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
//创建ArrayList集合
ArrayList<String> arrL1 = new ArrayList<>();
ArrayList<String> arrL2 = new ArrayList<>();
ArrayList<String> arrL3 = new ArrayList<>();
//添加ArrayList集合元素
arrL1.add("诸葛亮");
arrL1.add("周瑜");
arrL2.add("孙悟空");
arrL2.add("唐僧");
arrL3.add("林冲");
arrL3.add("武松");
//添加HashMap集合元素
hm.put("三国演义",arrL1);
hm.put("西游记",arrL2);
hm.put("水浒传",arrL3);
//遍历元素
Set<String> keySet = hm.keySet();
for (String key : keySet){
ArrayList<String> strings = hm.get(key);
for (String value : strings){
System.out.println(key+","+value);
}
}
}
}
输出结果:
水浒传,林冲
水浒传,武松
三国演义,诸葛亮
三国演义,周瑜
西游记,孙悟空
西游记,唐僧
5.10 案例
统计字符串中每个字符出现的次数
需求:键盘录入一个字符串,要求统计字符串中每个字符出现的次数
举例:键盘录入”aababcabcdabcde“ 在控制台输出:a(5)b(4)c(3)d(2)e(1)
package com.advanced.collection;
import java.util.Collection;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;
//统计字符串中每个字符出现的次数
//需求:键盘录入一个字符串,要求统计字符串中每个字符出现的次数
//举例:键盘录入”aababcabcdabcde“ 在控制台输出:a(5)b(4)c(3)d(2)e(1)
public class TreeMapDemo01 {
public static void main(String[] args) {
//创建集合对象
TreeMap<Character, Integer> treeMap = new TreeMap<Character, Integer>();
//创建扫描对象
Scanner scanner = new Scanner(System.in);
//提示输入字符串
System.out.println("请输入字符串:");
//获取录入字符串
String line = scanner.nextLine();
//遍历字符串,得到每个字符
for (int i = 0; i < line.length(); i++) {
char key = line.charAt(i);//将得到的字符作为键
Integer value = treeMap.get(key);//通过键获取值
if (value == null){//如果值为null,说明集合中没有该字符,就把该字符作为键,值为1
treeMap.put(key,1);
}else{//如果值不为null,说明集合中有该字符,就把该字符作为键,值+1
value++;
treeMap.put(key,value);
}
}
//遍历集合,得到键和值,按要求拼接
StringBuilder sb = new StringBuilder();
Set<Character> keySet = treeMap.keySet();
for (Character key:keySet){
Integer value = treeMap.get(key);
sb.append(key).append("(").append(value).append(")");
}
String result = sb.toString();//将StringBuilder类型转换为String类型
//输出结果
System.out.println(result);
scanner.close();
}
}
6.Collections
6.1 概述
Collections类的概述
是针对集合操作的工具类
Collections类的常用方法
public static <T extends Comparable<? super T>> void sort(List list):将指定的列表按升序排序
public static void reverse(List list):反转指定列表中元素的顺序
public static void shuffle(List list):使用默认的随机源随机排列指定的列表
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//Collections类常用方法
public class CollectionsDemo01 {
public static void main(String[] args) {
//创建集合对象
List<Integer> list = new ArrayList<Integer>();
//添加元素
list.add(20);
list.add(40);
list.add(10);
list.add(50);
list.add(30);
//输出元素
System.out.println(list);//[20, 40, 10, 50, 30]
//public static <T extends Comparable<? super T>> void sort(List<T> list):将指定的列表按升序排序
Collections.sort(list);
System.out.println(list);//[10, 20, 30, 40, 50]
//public static void reverse(List<T> list):反转指定列表中元素的顺序
Collections.reverse(list);
System.out.println(list);//[50, 40, 30, 20, 10]
//public static void shuffle(List<T> list):使用默认的随机源随机排列指定的列表
Collections.shuffle(list);
System.out.println(list);//[40, 20, 30, 10, 50] 随机排序,每次运行结果会不同
}
}
6.2 案例
ArrayList存储学生对象并遍历
需求:ArrayList存储学生对象,使用Collections对ArrayList进行排序
需求:按年龄从小到大进行排序,年龄相同时,按姓名字母排序
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
//ArrayList存储学生对象并遍历
//需求:ArrayList存储学生对象,使用Collections对ArrayList进行排序
// 需求:按年龄从小到大进行排序,年龄相同时,按姓名字母排序
public class CollectionsDemo02 {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> arrayList = new ArrayList<Student>();
//创建学生对象
Student s1 = new Student("linqingxia", 30);
Student s2 = new Student("zhangmanyu", 33);
Student s3 = new Student("wangzuxian", 32);
Student s4 = new Student("liuyan", 32);
//添加学生对象到集合
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
arrayList.add(s4);
//按年龄排序,年龄相同则按姓名字母排序
Collections.sort(arrayList, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num1 = s1.getAge() - s2.getAge();
int num2 = num1 == 0 ? s1.getName().compareTo(s2.getName()) : num1;
return num2;
}
});
//遍历集合
for (Student s : arrayList) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
输出结果:
linqingxia,30
liuyan,32
wangzuxian,32
zhangmanyu,33
6.3 案例:模拟斗地主
需求:通过程序实现斗地主过程的洗牌,发牌和看牌
package com.advanced.collection;
import java.util.ArrayList;
import java.util.Collections;
//案例:模拟斗地主
//需求:通过程序实现斗地主过程的洗牌,发牌和看牌
public class PokerDemo01 {
public static void main(String[] args) {
//创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现
ArrayList<String> array = new ArrayList<String>();
//往牌盒里面装牌
/*
♦2,♦3,...,♦K,♦A
♣2,...
♥2,...
♠2,...
小王,大王
*/
//定义花色数组
String[] colors = {"♦", "♣", "♥", "♠"};
//定义点数数组
String[] numbers = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
for (String color : colors) {
for (String number : numbers) {
array.add(color + number);
}
}
array.add("小王");
array.add("大王");
//洗牌,也就是把牌打散,用Collections的shuffle()方法实现
Collections.shuffle(array);
//发牌,也就是遍历集合,给三个玩家发牌
ArrayList<String> p1Array = new ArrayList<String>();
ArrayList<String> p2Array = new ArrayList<String>();
ArrayList<String> p3Array = new ArrayList<String>();
ArrayList<String> dpArray = new ArrayList<String>();
for (int i = 0; i < array.size(); i++) {
String poker = array.get(i);
if (i >= array.size() - 3) {
dpArray.add(poker);
} else if (i % 3 == 0) {
p1Array.add(poker);
} else if (i % 3 == 1) {
p2Array.add(poker);
} else if (i % 3 == 2) {
p3Array.add(poker);
}
}
//看牌,也就是三个玩家分别遍历自己的牌
lookPoker("玩家1",p1Array);
lookPoker("玩家2",p2Array);
lookPoker("玩家3",p3Array);
lookPoker("底牌",dpArray);
}
//看牌的方法
public static void lookPoker(String name, ArrayList<String> array){
System.out.print(name+"的牌:");
for (String poker: array){
System.out.print(poker+" ");
}
System.out.println();
}
}
输出结果:
玩家1的牌:♥9 ♠Q ♦K ♣6 ♦5 ♣Q ♠10 ♣10 ♦8 ♠5 ♠6 ♣9 ♠2 ♦7 ♦2 ♣8 ♦10
玩家2的牌:♥J ♣4 ♦9 ♠9 ♠8 ♠4 大王 ♥8 ♥2 ♦Q ♥A ♣5 ♥Q ♦6 ♥3 ♣2 ♠A
玩家3的牌:♠J ♦A ♥K ♣J ♥4 ♣A ♦3 ♣K ♥10 ♥6 ♠K ♠3 ♣3 ♣7 ♠7 ♦4 小王
底牌的牌:♥7 ♦J ♥5
6.4 案例:模拟斗地主升级版
需求:通过程序实现斗地主过程的洗牌,发牌和看牌。要求:对牌进行排序
package com.advanced.collection;
import java.util.*;
//需求:通过程序实现斗地主过程的洗牌,发牌和看牌。要求:对牌进行排序
public class PokerDemo02 {
public static void main(String[] args) {
//创建HashMap集合存储,键是索引(Integer),值是牌(String)
HashMap<Integer, String> hm = new HashMap<Integer, String>();
//创建ArrayList集合存储索引
ArrayList<Integer> array = new ArrayList<Integer>();
//创建花色数组
String[] colors = {"♦", "♣", "♥", "♠"};
//创建点数数组(按斗地主规则点数从小到大排序)
String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
//从0开始往HashMap中存储编号,并存储对应的值,同时往ArrayList里面存储编号
Integer index = 0;
for (String number : numbers) {
for (String color : colors) {
hm.put(index, color + number);
array.add(index);
index++;
}
}
hm.put(index, "小王");
array.add(index);
index++;
hm.put(index, "大王");
array.add(index);
//洗牌(洗的是编号),将索引顺序打散
Collections.shuffle(array);
//发牌(发的是编号,为了保证编号是有序的,创建TreeSet集合接收)
TreeSet<Integer> player1 = new TreeSet<Integer>();
TreeSet<Integer> player2 = new TreeSet<Integer>();
TreeSet<Integer> player3 = new TreeSet<Integer>();
TreeSet<Integer> dplayer = new TreeSet<Integer>();
for (int i = 0; i < array.size(); i++) {
Integer key = array.get(i);
if (i >= array.size() - 3) {
dplayer.add(key);
} else if (i % 3 == 0) {
player1.add(key);
} else if (i % 3 == 1) {
player2.add(key);
} else if (i % 3 == 2) {
player3.add(key);
}
}
//调用看牌方法
lookPoker("玩家1", player1, hm);
lookPoker("玩家2", player2, hm);
lookPoker("玩家3", player3, hm);
lookPoker("底牌", dplayer, hm);
}
//定义看牌方法(遍历TreeSet集合,获取编号,通过编号到HashMap中找对应值)
public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) {
System.out.print(name + "的牌:");
for (Integer key : ts) {
String value = hm.get(key);
System.out.print(value + " ");
}
System.out.println();
}
}
输出结果:
玩家1的牌:♠3 ♣4 ♥4 ♠4 ♦5 ♦6 ♦8 ♣8 ♣9 ♣10 ♠10 ♦J ♣Q ♠Q ♥K ♦A ♣A
玩家2的牌:♦3 ♥3 ♦4 ♣5 ♥5 ♣7 ♠8 ♥9 ♠9 ♥10 ♣J ♥Q ♣K ♠A ♦2 ♣2 小王
玩家3的牌:♣3 ♠5 ♣6 ♠6 ♥7 ♠7 ♥8 ♦10 ♥J ♠J ♦Q ♦K ♠K ♥A ♥2 ♠2 大王
底牌的牌:♥6 ♦7 ♦9