Java基础系列–集合
一、集合框架 #
1、学习集合的目标: #
- 1.会使用集合存储数据
- 2.会遍历集合,把数据取出来
- 3.掌握每种集合的特性
2、集合框架的学习方式 #
- 1.学习顶层:学习顶层接口/抽象类中共性的方法,所有的子类都可以使用
- 2.使用底层:底层不是接口就是抽象类,无法创建对象使用,需要使用底层的子类创建对象使用
二、Collection常用功能 #
共性的方法:
public boolean add(E e)
:把给定的对象添加到当前集合中。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
:把给定的对象在当前集合中删除。public boolean contains(E e)
:判断当前集合中是否包含给定的对象。public boolean isEmpty()
:判断当前集合是否为空。public int size()
:返回集合中元素的个数。public Object[] toArray()
:把集合中的元素,存储到数组中。(数组可以进行for循环遍历)
注:没有索引都不可以用for循环遍历,可以用Iterator迭代器遍历。(如Collection就没有索引)
package com.anarkh.demo01.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
/*
java.util.Collection接口
所有单列集合的最顶层的接口,里边定义了所有单列集合共性的方法
任意的单列集合都可以使用Collection接口中的方法
共性的方法:
public boolean add(E e): 把给定的对象添加到当前集合中 。
public void clear() :清空集合中所有的元素。
public boolean remove(E e): 把给定的对象在当前集合中删除。
public boolean contains(E e): 判断当前集合中是否包含给定的对象。
public boolean isEmpty(): 判断当前集合是否为空。
public int size(): 返回集合中元素的个数。
public Object[] toArray(): 把集合中的元素,存储到数组中。
*/
public class Demo01Collection {
public static void main(String[] args) {
//创建集合对象,可以使用多态
//Collection<String> coll = new ArrayList<>();
Collection<String> coll = new HashSet<>();
System.out.println(coll);//重写了toString方法 []
/*
public boolean add(E e): 把给定的对象添加到当前集合中 。
返回值是一个boolean值,一般都返回true,所以可以不用接收
*/
boolean b1 = coll.add("张三");
System.out.println("b1:"+b1);//b1:true
System.out.println(coll);//[张三]
coll.add("李四");
coll.add("李四");
coll.add("赵六");
coll.add("田七");
System.out.println(coll);//[张三, 李四, 赵六, 田七]
/*
public boolean remove(E e): 把给定的对象在当前集合中删除。
返回值是一个boolean值,集合中存在元素,删除元素,返回true
集合中不存在元素,删除失败,返回false
*/
boolean b2 = coll.remove("赵六");
System.out.println("b2:"+b2);//b2:true
boolean b3 = coll.remove("赵四");
System.out.println("b3:"+b3);//b3:false
System.out.println(coll);//[张三, 李四, 田七]
/*
public boolean contains(E e): 判断当前集合中是否包含给定的对象。
包含返回true
不包含返回false
*/
boolean b4 = coll.contains("李四");
System.out.println("b4:"+b4);//b4:true
boolean b5 = coll.contains("赵四");
System.out.println("b5:"+b5);//b5:false
//public boolean isEmpty(): 判断当前集合是否为空。 集合为空返回true,集合不为空返回false
boolean b6 = coll.isEmpty();
System.out.println("b6:"+b6);//b6:false
//public int size(): 返回集合中元素的个数。
int size = coll.size();
System.out.println("size:"+size);//size:3
//public Object[] toArray(): 把集合中的元素,存储到数组中。(数组是可以遍历的)
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//public void clear() :清空集合中所有的元素。但是不删除集合,集合还存在
coll.clear();
System.out.println(coll);//[]
System.out.println(coll.isEmpty();//true
}
}
三、Iterator迭代器 #
java.util.Iterator
1、Iterator #
1.概念:
- 迭代,即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
2.两个常用的方法:
boolean hasNext()
:如果仍有元素可以迭代,则返回true。判断集合中还有没有下一个元素,有就返回true,没有就返回false。E next()
:返回迭代的下一个元素。取出集合中的下一个元素。
3.获取Iterator的实现类对象:
Iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊,Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象。
Iterator<E> iterator()
:返回在此collection的元素上进行迭代的迭代器。
4.迭代器的使用步骤(重点):
- 1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)。
- 2.使用Iterator接口中的方法hasNext()判断还有没有下一个元素。
- 3.使用Iterator接口中的方法next()取出集合中的下一个元素。
5.迭代器的实现原理:
- 在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo01Iterator {
public static void main(String[] args) {
//创建一个集合对象
Collection<String> coll = new ArrayList<>();
//往集合中添加元素
coll.add("姚明");
coll.add("科比");
coll.add("麦迪");
coll.add("詹姆斯");
coll.add("艾弗森");
/*
1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
注意:
Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
*/
//多态 接口 实现类对象
Iterator<String> it = coll.iterator();
/*
发现使用迭代器取出集合中元素的代码,是一个重复的过程
所以我们可以使用循环优化
不知道集合中有多少元素,使用while循环
循环结束的条件,hasNext方法返回false
*/
while(it.hasNext(){
String e = it.next();
System.out.println(e);
}
System.out.println("----------------------");
for(Iterator<String> it2 = coll.iterator();it2.hasNext();){
String e = it2.next();
System.out.println(e);
}
}
}
2、增强for循环 #
1.介绍:
- 增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写。是JDK1.5之后出现的新特性。
Collection<E> extends Iterable<E>
:所有的单列集合都可以使用增强for循环。public interface Iterable<T>
:实现这个接口允许对象成为“foreach”语句的目标。
2.作用:
- 增强for循环:用来遍历集合和数组。
- Iterator迭代器不能遍历数组。
3.格式:
for(集合/数组的数据类型 变量名 : 集合名/数组名){ sout(变量名); }
public class Demo02Foreach {
public static void main(String[] args) {
demo02();
}
//使用增强for循环遍历集合
private static void demo02() {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
for(String s : list){
System.out.println(s);
}
}
//使用增强for循环遍历数组
private static void demo01() {
int[] arr = {1,2,3,4,5};
for(int i:arr){
System.out.println(i);
}
}
}
四、泛型 #
1、泛型的概念 #
- 泛型:是一种位置的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型。泛型也可以看成是一个变量,用来接收数据类型
- E e:Element元素
- T t:Type类型
2、使用泛型的好处 #
从创建集合使用泛型与不适用泛型的角度出发:
- 创建集合对象,使用泛型:
- 好处:
- 1.避免了类型转化的麻烦,存储的是什么类型,取出的就是什么类型。
- 2.把运行期异常(代码运行之后会抛出的异常),提升到了编译器(写代码的时候会报错)。
- 弊端:
- 泛型是什么类型,只能存储什么类型的数据。
- 好处:
- 创建集合对象,不适用泛型:
- 好处:
- 集合不使用泛型,默认的类型就是Object,可以存储任意类型的数据。
- 弊端:
- 不安全,会引发异常。
- 好处:
package com.anarkh.demo03.Generic;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo01Generic {
public static void main(String[] args) {
show02();
}
/*
创建集合对象,使用泛型
好处:
1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错)
弊端:
泛型是什么类型,只能存储什么类型的数据
*/
private static void show02() {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
//list.add(1);//add(java.lang.String)in ArrayList cannot be applied to (int)
//使用迭代器遍历list集合
Iterator<String> it = list.iterator();
while(it.hasNext(){
String s = it.next();
System.out.println(s+"->"+s.length();
}
}
/*
创建集合对象,不使用泛型
好处:
集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
弊端:
不安全,会引发异常
*/
private static void show01() {
ArrayList list = new ArrayList();
list.add("abc");
list.add(1);
//使用迭代器遍历list集合
//获取迭代器
Iterator it = list.iterator();
//使用迭代器中的方法hasNext和next遍历集合
while(it.hasNext(){
//取出元素也是Object类型
Object obj = it.next();
System.out.println(obj);
//想要使用String类特有的方法,length获取字符串的长度;不能使用 多态 Object obj = "abc";
//需要向下转型
//会抛出ClassCastException类型转换异常,不能把Integer类型转换为String类型
String s = (String)obj;
System.out.println(s.length();
}
}
}
3、泛型的使用 #
1.定义和使用含有泛型的类
- 创建对象的时候确定泛型的数据类型。
public class GenericClass<E> { private E name; public E getName() { return name; } public void setName(E name) { this.name = name; } }
public class Demo02GenericClass { public static void main(String[] args) { //不写泛型默认为Object类型 GenericClass gc = new GenericClass(); gc.setName("只能是字符串"); Object obj = gc.getName(); //创建GenericClass对象,泛型使用Integer类型 GenericClass<Integer> gc2 = new GenericClass<>(); gc2.setName(1); Integer name = gc2.getName(); System.out.println(name); //创建GenericClass对象,泛型使用String类型 GenericClass<String> gc3 = new GenericClass<>(); gc3.setName("小明"); String name1 = gc3.getName(); System.out.println(name1); } }
2.定义和使用含有泛型的方法
泛型定义在方法的修饰符和返回值类型之间。
格式
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型){ 方法体; }
含有泛型的方法,在调用方法的时候确定泛型的数据库类型。传递什么类型的参数,泛型就是什么类型。
public class GenericMethod { //定义一个含有泛型的方法 public <M> void method01(M m){ System.out.println(m); } //定义一个含有泛型的静态方法 public static <S> void method02(S s){ System.out.println(s); } }
public class Demo03GenericMethod { public static void main(String[] args) { //创建GenericMethod对象 GenericMethod gm = new GenericMethod(); /* 调用含有泛型的方法method01 传递什么类型,泛型就是什么类型 */ gm.method01(10); gm.method01("abc"); gm.method01(8.8); gm.method01(true); gm.method02("静态方法,不建议创建对象使用"); //静态方法,通过类名.方法名(参数)可以直接使用 GenericMethod.method02("静态方法"); GenericMethod.method02(1); } }
3.定义和使用含有泛型的接口
含有泛型的接口有两种方式:
- 1.定义接口的实现类,实现接口,指定接口的泛型。
- 2.接口使用什么泛型,实现类就使用什么泛型,类跟着接口走,就相当于定义了一个含有类型的类,创建对象的时候确定泛型的类型。
/* 定义含有泛型的接口 */ public interface GenericInterface<I> { public abstract void method(I i); }
/* 含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型 public interface Iterator<E> { E next(); } Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String public final class Scanner implements Iterator<String>{ public String next() {} } */ public class GenericInterfaceImpl1 implements GenericInterface<String>{ @Override public void method(String s) { System.out.println(s); } }
/* 含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走 就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型 public interface List<E>{ boolean add(E e); E get(int index); } public class ArrayList<E> implements List<E>{ public boolean add(E e) {} public E get(int index) {} } */ public class GenericInterfaceImpl2<I> implements GenericInterface<I> { @Override public void method(I i) { System.out.println(i); } }
/* 测试含有泛型的接口 */ public class Demo04GenericInterface { public static void main(String[] args) { //创建GenericInterfaceImpl1对象 GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1(); gi1.method("字符串"); //创建GenericInterfaceImpl2对象 GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>(); gi2.method(10); GenericInterfaceImpl2<Double> gi3 = new GenericInterfaceImpl2<>(); gi3.method(8.8); } }
4、泛型通配符 #
1.通配符的基本使用
- a.概念
- ?:代表任意的数据类型
- b.使用方式:
- 不能创建对象使用
- 只能作为方法的参数使用
public class Demo05Generic { public static void main(String[] args) { ArrayList<Integer> list01 = new ArrayList<>(); list01.add(1); list01.add(2); ArrayList<String> list02 = new ArrayList<>(); list02.add("a"); list02.add("b"); printArray(list01); printArray(list02); //ArrayList<?> list03 = new ArrayList<?>(); } /* 定义一个方法,能遍历所有类型的ArrayList集合 这时候我们不知道ArrayList集合使用什么数据类型,可以泛型的通配符?来接收数据类型 注意: 泛型没有继承概念的 */ public static void printArray(ArrayList<?> list){ //使用迭代器遍历集合 Iterator<?> it = list.iterator(); while(it.hasNext(){ //it.next()方法,取出的元素是Object,可以接收任意的数据类型 Object o = it.next(); System.out.println(o); } } }
- a.概念
2.通配符的高级使用
- a.泛型的上限限定:
? extends E
:代表使用的泛型只能是E类型的子类/本身。
- b.泛型的下限限定:
? super E
:代表使用的泛型只能是E类型的父类/本身。
public class Demo06Generic { public static void main(String[] args) { Collection<Integer> list1 = new ArrayList<Integer>(); Collection<String> list2 = new ArrayList<String>(); Collection<Number> list3 = new ArrayList<Number>(); Collection<Object> list4 = new ArrayList<Object>(); getElement1(list1); //getElement1(list2);//报错 getElement1(list3); //getElement1(list4);//报错 //getElement2(list1);//报错 //getElement2(list2);//报错 getElement2(list3); getElement2(list4); /* 类与类之间的继承关系 Integer extends Number extends Object String extends Object */ } // 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类 public static void getElement1(Collection<? extends Number> coll){} // 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类 public static void getElement2(Collection<? super Number> coll){} }
- a.泛型的上限限定:
五、斗地主案例 #
package com.anarkh.demo04.Test;
import java.util.ArrayList;
import java.util.Collections;
/*
斗地主综合案例:
1.准备牌
2.洗牌
3.发牌
4.看牌
*/
public class DouDiZhu {
public static void main(String[] args) {
//1.准备牌
//定义一个存储54张牌的ArrayList集合,泛型使用String
ArrayList<String> poker = new ArrayList<>();
//定义两个数组,一个数组存储牌的花色,一个数组存储牌的序号
String[] colors = {"♠","♥","♣","♦"};
String[] numbers = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
//先把大王和小王存储到poker集合中
poker.add("大王");
poker.add("小王");
//循环嵌套遍历两个数组,组装52张牌
for(String number : numbers){
for (String color : colors) {
//System.out.println(color+number);
//把组装好的牌存储到poker集合中
poker.add(color+number);
}
}
//System.out.println(poker);
/*
2.洗牌
使用集合的工具类Collections中的方法
static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换。
*/
Collections.shuffle(poker);
//System.out.println(poker);
/*
3.发牌
*/
//定义4个集合,存储玩家的牌和底牌
ArrayList<String> player01 = new ArrayList<>();
ArrayList<String> player02 = new ArrayList<>();
ArrayList<String> player03 = new ArrayList<>();
ArrayList<String> diPai = new ArrayList<>();
/*
遍历poker集合,获取每一张牌
使用poker集合的索引%3给3个玩家轮流发牌
剩余3张牌给底牌
注意:
先判断底牌(i>=51),否则牌就发没了
*/
for (int i = 0; i < poker.size() ; i++) {
//获取每一张牌
String p = poker.get(i);
//轮流发牌
if(i>=51){
//给底牌发牌
diPai.add(p);
}else if(i%3==0){
//给玩家1发牌
player01.add(p);
}else if(i%3==1){
//给玩家2发牌
player02.add(p);
}else if(i%3==2){
//给玩家3发牌
player03.add(p);
}
}
//4.看牌
System.out.println("刘德华:"+player01);
System.out.println("周润发:"+player02);
System.out.println("周星驰:"+player03);
System.out.println("底牌:"+diPai);
}
}
六、与集合有关的数据结构 #
1、栈 #
- 先进后出
2、队列 #
- 先进先出
3、数组 #
- 数组的特点:
- 查询快:数组的地址是连续的,我们通过数组的首地址可以找到数组,通过数组的索引可以快速查找某一个元素。
- 增删慢:数组的长度是固定的,我们想要增加/删除一个元素,必须创建一个新数组,把源数组的数据复制过来。
4、链表 #
- 链表的特点:
- 查询慢:链表中地址不是连续的,每次查询元素,都必须从头开始查询。
- 增删快:链表结构,增加/删除一个元素,对链表的整体结构没有影响,所以增删快。
- 链表的构成:
- 链表中的每一个元素也称之为一个节点,一个节点包含了一个数据源(存储数组),两个指针域(存储地址)。
- 链表的分类:
- 单向链表:链表中只有一条链子,不能保证元素的顺序(存储元素和取出元素的顺序有可能不一致)。
- 双向链表:链表中有两条链子,有一条链子是专门记录元素的顺序,是一个有序的集合。
5、红黑树 #
- 二叉树:
- 每个节点不超过2的有序树。
- 排序树/有序树:
- 在二叉树的基础上,元素是有大小顺序的:左子树小,右子树大。
- 平衡树:
- 左孩子和右孩子相等。
- 不平衡树:
- 左孩子和右孩子不相等。
- 红黑树:
- 特点:
- 趋近于平衡树,查询的速度非常的快,查询叶子节点最大次数和最小次数不能超过2倍。
- 约束:
- 1.节点可以是红色的或者黑色的。
- 2.根节点是黑色的。
- 3.叶子节点(空节点)是黑色的。
- 4.每个红色的节点的子节点都是黑色的。
- 5.任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
- 特点:
七、List #
1、List集合 #
java.util.List接口 extends Collection几口
List接口的特点
- 1.有序的集合,存储元素和取出元素的顺序是一致的(存储123,取出123)。
- 2.有索引,包含了一些带索引的方法。
- 3.允许存储重复的元素
List接口中带索引的方法(特有)
public void add(int index,E element)
:将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
:移除列表中指定位置的元素,返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
注意:
- 操作索引的时候,一定要防止索引越界异常:
IndexOutOfBoundsException
:索引越界异常,集合会报ArrayIndexOutOfBoundsException
:数组索引越界异常StringIndexOutOfBoundsException
:字符串索引越界异常
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Demo01List { public static void main(String[] args) { //创建一个List集合对象,多态 List<String> list = new ArrayList<>(); //使用add方法往集合中添加元素 list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("a"); //打印集合 System.out.println(list);//[a, b, c, d, a] 不是地址重写了toString //public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。 //在c和d之间添加一个itheima list.add(3,"itheima");//[a, b, c, itheima, d, a] System.out.println(list); //public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。 //移除元素 String removeE = list.remove(2); System.out.println("被移除的元素:"+removeE);//被移除的元素:c System.out.println(list);//[a, b, itheima, d, a] //public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。 //把最后一个a,替换为A String setE = list.set(4, "A"); System.out.println("被替换的元素:"+setE);//被替换的元素:a System.out.println(list);//[a, b, itheima, d, A] //List集合遍历有3种方式 //使用普通的for循环 for(int i=0; i<list.size(); i++){ //public E get(int index):返回集合中指定位置的元素。 String s = list.get(i); System.out.println(s); } System.out.println("-----------------"); //使用迭代器 Iterator<String> it = list.iterator(); while(it.hasNext(){ String s = it.next(); System.out.println(s); } System.out.println("-----------------"); //使用增强for for (String s : list) { System.out.println(s); } String r = list.get(5);//IndexOutOfBoundsException: Index 5 out-of-bounds for length 5 System.out.println(r); } }
- 操作索引的时候,一定要防止索引越界异常:
2、ArrayList集合 #
java.util.ArrayList
集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList
是最常用的集合。许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
3、LinkedList集合 #
java.util.LinkedList集合 implements List接口
LinkedList集合的特点:
- 1.底层是一个链表结构:查询慢,增删快。
- 2.里面包含了大量操作首尾元素的方法
注意:使用LinkedList集合特有的方法,不能使用多态。
LinkedList特有的方法:
public void addFirst(E e)
:将指定元素插入此列表的开头。public void addLast(E e)
:将指定元素添加到此列表的结尾。public void push(E e)
:将元素推入此列表所表示的堆栈。public E getFirst()
:返回此列表的第一个元素。public E getLast()
:返回此列表的最后一个元素。public E removeFirst()
:移除并返回此列表的第一个元素。public E removeLast()
:移除并返回此列表的最后一个元素。public E pop()
:从此列表所表示的堆栈处弹出一个元素。public boolean isEmpty()
:如果列表不包含元素,则返回true。
import java.util.LinkedList; public class Demo02LinkedList { public static void main(String[] args) { show03(); } /* - public E removeFirst():移除并返回此列表的第一个元素。 - public E removeLast():移除并返回此列表的最后一个元素。 - public E pop():从此列表所表示的堆栈处弹出一个元素。此方法相当于 removeFirst */ private static void show03() { //创建LinkedList集合对象 LinkedList<String> linked = new LinkedList<>(); //使用add方法往集合中添加元素 linked.add("a"); linked.add("b"); linked.add("c"); System.out.println(linked);//[a, b, c] //String first = linked.removeFirst(); String first = linked.pop(); System.out.println("被移除的第一个元素:"+first); String last = linked.removeLast(); System.out.println("被移除的最后一个元素:"+last); System.out.println(linked);//[b] } /* - public E getFirst():返回此列表的第一个元素。 - public E getLast():返回此列表的最后一个元素。 */ private static void show02() { //创建LinkedList集合对象 LinkedList<String> linked = new LinkedList<>(); //使用add方法往集合中添加元素 linked.add("a"); linked.add("b"); linked.add("c"); //linked.clear();//清空集合中的元素 在获取集合中的元素会抛出NoSuchElementException //public boolean isEmpty():如果列表不包含元素,则返回true。 if(!linked.isEmpty(){ String first = linked.getFirst(); System.out.println(first);//a String last = linked.getLast(); System.out.println(last);//c } } /* - public void addFirst(E e):将指定元素插入此列表的开头。 - public void addLast(E e):将指定元素添加到此列表的结尾。 - public void push(E e):将元素推入此列表所表示的堆栈。此方法等效于 addFirst(E)。 */ private static void show01() { //创建LinkedList集合对象 LinkedList<String> linked = new LinkedList<>(); //使用add方法往集合中添加元素 linked.add("a"); linked.add("b"); linked.add("c"); System.out.println(linked);//[a, b, c] //public void addFirst(E e):将指定元素插入此列表的开头。 //linked.addFirst("www"); linked.push("www"); System.out.println(linked);//[www, a, b, c] //public void addLast(E e):将指定元素添加到此列表的结尾。此方法等效于 add() linked.addLast("com"); System.out.println(linked);//[www, a, b, c, com] } }
八、Set #
1、HashSet集合介绍 #
java.util.Set接口 extends Colleciton接口
Set接口的特点:
- 1.不允许存储重复的元素。
- 2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历。
java.util.HashSet集合 implements Set接口
HashSet特点:
- 1.不允许存储重复的元素。
- 2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历。
- 3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致。
- 4.底层是一个哈希表结构(查询的速度非常的快)。
import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class Demo01Set { public static void main(String[] args) { Set<Integer> set = new HashSet<>(); //使用add方法往集合中添加元素 set.add(1); set.add(3); set.add(2); set.add(1); //使用迭代器遍历set集合 Iterator<Integer> it = set.iterator(); while (it.hasNext(){ Integer n = it.next(); System.out.println(n);//1,2,3 } //使用增强for遍历set集合 System.out.println("-----------------"); for (Integer i : set) { System.out.println(i); } } }
2、哈希值 #
哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)。
在Object类有一个方法,可以获取对象的哈希值:
int hashCode()
:返回该对象的哈希码值hashCode方法的源码:
public native int hashCode();
- native:代表该方法调用的是本地操作系统的方法。
public class Demo01HashCode { public static void main(String[] args) { //Person类继承了Object类,所以可以使用Object类的hashCode方法 Person p1 = new Person(); int h1 = p1.hashCode(); System.out.println(h1);//1967205423 | 1 Person p2 = new Person(); int h2 = p2.hashCode(); System.out.println(h2);//42121758 | 1 /* toString方法的源码: return getClass().getName() + "@" + Integer.toHexString(hashCode(); */ System.out.println(p1);//com.itheima.demo03.hashCode.Person@75412c2f System.out.println(p2);//com.itheima.demo03.hashCode.Person@282ba1e System.out.println(p1==p2);//false /* String类的哈希值 String类重写Obejct类的hashCode方法 */ String s1 = new String("abc"); String s2 = new String("abc"); System.out.println(s1.hashCode();//96354 System.out.println(s2.hashCode();//96354 System.out.println("重地".hashCode();//1179395 System.out.println("通话".hashCode();//1179395 } }
public class Person extends Object{ //重写hashCode方法 @Override public int hashCode() { return 1; } }
3、哈希表–HashSet集合存储数据的结构 #
HashSet集合存储数据的结构:哈希表
哈希表的特点:速度快。
哈希表
- 1.JDK1.8之前,哈希表底层采用
数组+链表
实现。- 使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值一次查找的效率会低。
- 2.JDK1.8之后,哈希表底层采用
数组+链表+红黑树
实现。- 当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
- 1.JDK1.8之前,哈希表底层采用
4、Set集合存储元素不重复的原理 #
HashSet<String> set = new HashSet<>();
String s1 = new String("abc");
String s2 = new String("abc");
set.add(s1);
set.add(s2);
set.add("重地");
set.add("通话");
set.add("abc");
System.out.println(set);[重地,通话,abc]
Set集合在调用add方法的时候,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复。
- add方法会调用s1的hashCode方法,计算字符串"abc"的哈希值,哈希值是96354,在集合中找有没有96354这个哈希值的元素,发现没有,就会把s1存储到集合中。
- add方法会调用s2的hashCode方法,计算字符串"abc"的哈希值,哈希值是96354,在集合中找有没有96354这个哈希值的元素,发现有(哈希冲突),s2会调用equals方法和哈希值相同的元素进行比较,
s2.equals(s1)
,返回true,两个元素的哈希值相同,equals方法返回true,认定两个元素相同,就不会把s2存储到集合中。 - add方法会调用"重地"的hashCode方法,计算字符串"重地"的哈希值,哈希值是1179395,在集合中找有没有1179395这个哈希值的元素,发现没有,就会把"重地"存储到集合中。
- add方法会调用"通话"的hashCode方法,计算字符串"通话"的哈希值,哈希值是1179395,在集合中找有没有1179395这个哈希值的元素,发现有(哈希冲突),“通话"会调用equals方法和哈希值相同的元素进行比较,
"通话".equals("重地")
,返回true,两个元素的哈希值不同,equals方法返回false,认定两个元素不同,就会把"通话"存储到集合中。
如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
5、HashSet存储自定义类型元素 #
使用HashSet存储自定义类型元素,如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
IDEA快捷键:
- Alt+Insert–>equals() and hashCode()
package com.anarkh.demo02.Set; 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; } @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 hashCode() { return Objects.hash(name, age); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", 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; } }
package com.anarkh.demo02.Set; import java.util.HashSet; /* HashSet存储自定义类型元素 set集合报错元素唯一: 存储的元素(String,Integer,...Student,Person...),必须重写hashCode方法和equals方法 要求: 同名同年龄的人,视为同一个人,只能存储一次 */ public class Demo03HashSetSavePerson { public static void main(String[] args) { //创建HashSet集合存储Person HashSet<Person> set = new HashSet<>(); Person p1 = new Person("小美女",18); Person p2 = new Person("小美女",18); Person p3 = new Person("小美女",19); System.out.println(p1.hashCode();//1967205423 System.out.println(p2.hashCode();//42121758 System.out.println(p1==p2);//false System.out.println(p1.equals(p2);//false set.add(p1); set.add(p2); set.add(p3); System.out.println(set); } }
6、LinkedHashSet集合 #
java.util.LinkedHashSet集合 extends HashSet集合
LinkedHashSet集合特点:
- 底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序。
HashSet和LinkedHashSet区别:
- 1.HashSet:无序,不允许重复。
- LinkedHashSet:有序,不允许重复。
import java.util.HashSet; import java.util.LinkedHashSet; public class Demo04LinkedHashSet { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("www"); set.add("abc"); set.add("abc"); set.add("itcast"); System.out.println(set);//[abc, www, itcast] 无序,不允许重复 LinkedHashSet<String> linked = new LinkedHashSet<>(); linked.add("www"); linked.add("abc"); linked.add("abc"); linked.add("itcast"); System.out.println(linked);//[www, abc, itcast] 有序,不允许重复 } }
7、可变参数 #
可变参数:是JDK1.5之后出现的新特性。
使用前提:
- 当方法的参数列表数据类型已经确定,但是参数的个数不确定的时候,就可以使用可变参数。
使用格式:
修饰符 返回值类型 方法名(数据类型...变量名){}
可变参数的原理:
- 可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数传递的参数个数,可以是0个(不传递),1,2…多个。
可变参数的注意事项:
- 1.一个方法的参数列表,只能有一个可变参数。
- 2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾。
public class Demo01VarArgs { public static void main(String[] args) { //int i = add(); //int i = add(10); int i = add(10,20); //int i = add(10,20,30,40,50,60,70,80,90,100); System.out.println(i); method("abc",5.5,10,1,2,3,4); } /* 可变参数的注意事项 1.一个方法的参数列表,只能有一个可变参数 2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾 */ /*public static void method(int...a,String...b){ }*/ /*public static void method(String b,double c,int d,int...a){ }*/ //可变参数的特殊(终极)写法 public static void method(Object...obj){ } /* 定义计算(0-n)整数和的方法 已知:计算整数的和,数据类型已经确定int 但是参数的个数不确定,不知道要计算几个整数的和,就可以使用可变参数 add(); 就会创建一个长度为0的数组, new int[0] add(10); 就会创建一个长度为1的数组,存储传递来过的参数 new int[]{10}; add(10,20); 就会创建一个长度为2的数组,存储传递来过的参数 new int[]{10,20}; add(10,20,30,40,50,60,70,80,90,100); 就会创建一个长度为2的数组,存储传递来过的参数 new int[]{10,20,30,40,50,60,70,80,90,100}; */ public static int add(int...arr){ //System.out.println(arr);//[I@2ac1fdc4 底层是一个数组 //System.out.println(arr.length);//0,1,2,10 //定义一个初始化的变量,记录累加求和 int sum = 0; //遍历数组,获取数组中的每一个元素 for (int i : arr) { //累加求和 sum += i; } //把求和结果返回 return sum; } //定义一个方法,计算三个int类型整数的和 /*public static int add(int a,int b,int c){ return a+b+c; }*/ //定义一个方法,计算两个int类型整数的和 /*public static int add(int a,int b){ return a+b; }*/ }
九、Collections工具类 #
java.utils.Collections
是集合工具类,用来对集合进行操作。
public static <T> boolean addAll(Collection<T> c, T...elements)
:往集合中添加一些元素。public static void shuffle(List<?> list)
:打乱顺序:打乱集合顺序。public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); //public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素。 Collections.addAll(list,"a","b","c","d","e"); System.out.println(list);//[a, b, c, d, e] //public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。 Collections.shuffle(list); System.out.println(list);//[b, d, c, a, e], [b, d, c, a, e] }
public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。- 注意:
sort(List<T> list)
:使用前提:- 被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则。
- Comparable接口的排序规则:自己(this)-参数:升序。
public class Demo02Sort { public static void main(String[] args) { ArrayList<Integer> list01 = new ArrayList<>(); list01.add(1); list01.add(3); list01.add(2); System.out.println(list01);//[1, 3, 2] //public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。 Collections.sort(list01);//默认是升序 System.out.println(list01);//[1, 2, 3] ArrayList<String> list02 = new ArrayList<>(); list02.add("a"); list02.add("c"); list02.add("b"); System.out.println(list02);//[a, c, b] Collections.sort(list02); System.out.println(list02);//[a, b, c] ArrayList<Person> list03 = new ArrayList<>(); list03.add(new Person("张三",18); list03.add(new Person("李四",20); list03.add(new Person("王五",15); System.out.println(list03);//[Person{name='张三', age=18}, Person{name='李四', age=20}, Person{name='王五', age=15}] Collections.sort(list03); System.out.println(list03); } }
package com.anarkh.demo05.Collections; 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; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", 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 int compareTo(Person o) { //return 0;//认为元素都是相同的 //自定义比较的规则,比较两个人的年龄(this,参数Person) //return this.getAge() - o.getAge();//年龄升序排序 return o.getAge() - this.getAge();//年龄升序排序 } }
- 注意:
public static <T> void sort(List<T> list, Comparator<? super T>)
:将集合中元素按照指定规则排序。- Comparator和Comparable区别:
- Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法。
- Comparator:相当于找一个第三方的裁判,比较两个。
- Comparator的排序规则:o1-o2:升序。
public class Demo03Sort { public static void main(String[] args) { ArrayList<Integer> list01 = new ArrayList<>(); list01.add(1); list01.add(3); list01.add(2); System.out.println(list01);//[1, 3, 2] Collections.sort(list01, new Comparator<Integer>() { //重写比较的规则 @Override public int compare(Integer o1, Integer o2) { //return o1-o2;//升序 return o2-o1;//降序 } }); System.out.println(list01); ArrayList<Student> list02 = new ArrayList<>(); list02.add(new Student("a迪丽热巴",18); list02.add(new Student("古力娜扎",20); list02.add(new Student("杨幂",17); list02.add(new Student("b杨幂",18); System.out.println(list02); /*Collections.sort(list02, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { //按照年龄升序排序 return o1.getAge()-o2.getAge(); } });*/ //扩展:了解 Collections.sort(list02, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { //按照年龄升序排序 int result = o1.getAge()-o2.getAge(); //如果两个人年龄相同,再使用姓名的第一个字比较 if(result==0){ result = o1.getName().charAt(0)-o2.getName().charAt(0); } return result; } }); System.out.println(list02); } }
package com.anarkh.demo05.Collections; public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", 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; } }
- Comparator和Comparable区别:
十、Map集合 #
1、Map集合概述 #
java.util.Map<k,v>集合
Map集合的特点:
- 1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)。
- 2.Map集合中的元素,key和value的数据类型可以相同,也可以不同。
- 3.Map集合中的元素,key是不允许重复的,value是可以重复的。
- 4.Map集合中的元素,key和value是一一对应。
java.util.HashMap<k,v> implements Map<k,v>接口
HashMap集合的特点:
- 1.HashMap集合底层是哈希表:查询单 速度特别的快。
- JDK1.8之前:数组+单向链表
- JDK1.8之后:数组+单向链表/红黑树(链表的长度超过8):提高查询的速度
- 2.HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致。
java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
- 1.HashMap集合底层是哈希表:查询单 速度特别的快。
LinkedHashMap的特点:
- 1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
- 2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
2、Map接口中的常用方法 #
public V put(K key, V value)
:把指定的键与指定的值添加到Map集合中。- 返回值:v
- 存储键值对的时候,key不重复,返回值V是null
- 存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值
private static void show01() { //创建Map集合对象,多态 Map<String,String> map = new HashMap<>(); String v1 = map.put("李晨", "范冰冰1"); System.out.println("v1:"+v1);//v1:null String v2 = map.put("李晨", "范冰冰2"); System.out.println("v2:"+v2);//v2:范冰冰1 System.out.println(map);//{李晨=范冰冰2} map.put("冷锋","龙小云"); map.put("杨过","小龙女"); map.put("尹志平","小龙女"); System.out.println(map);//{杨过=小龙女, 尹志平=小龙女, 李晨=范冰冰2, 冷锋=龙小云} }
- 返回值:v
public V remove(Object key)
:把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值。- 返回值:V
- key存在,v返回被删除的值
- key不存在,v返回null
private static void show02() { //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); System.out.println(map);//{林志玲=178, 赵丽颖=168, 杨颖=165} Integer v1 = map.remove("林志玲"); System.out.println("v1:"+v1);//v1:178 System.out.println(map);//{赵丽颖=168, 杨颖=165} //int v2 = map.remove("林志颖");//自动拆箱 NullPointerException Integer v2 = map.remove("林志颖"); System.out.println("v2:"+v2);//v2:null System.out.println(map);//{赵丽颖=168, 杨颖=165} }
- 返回值:V
public V get(Object key)
:根据指定的键,在Map集合中获取对应的值。- 返回值:
- key存在,返回对应的value值
- key不存在,返回null
private static void show03() { //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); Integer v1 = map.get("杨颖"); System.out.println("v1:"+v1);//v1:165 Integer v2 = map.get("迪丽热巴"); System.out.println("v2:"+v2);//v2:null }
- 返回值:
boolean containsKey(Object key)
:判断集合中是否包含指定的键。- 返回值:
- 包含返回true
- 不包含返回false
private static void show04() { //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); boolean b1 = map.containsKey("赵丽颖"); System.out.println("b1:"+b1);//b1:true boolean b2 = map.containsKey("赵颖"); System.out.println("b2:"+b2);//b2:false }
- 返回值:
3、Map集合遍历键找值方式(keySet) #
Map集合的第一种遍历方式:通过键找值的方式
Map集合中的方法:
Set<K> keySet()
:返回此映射中包含的键的Set视图。
实现步骤:
- 1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中。
- 2.遍历set集合,获取Map集合中的每一个key。
- 3.通过Map集合中的方法get(key),通过key找到value。
public static void main(String[] args) { //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); //1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中 Set<String> set = map.keySet(); //2.遍历set集合,获取Map集合中的每一个key //使用迭代器遍历Set集合 Iterator<String> it = set.iterator(); while (it.hasNext(){ String key = it.next(); //3.通过Map集合中的方法get(key),通过key找到value Integer value = map.get(key); System.out.println(key+"="+value); } System.out.println("-------------------"); //使用增强for遍历Set集合 for(String key : set){ //3.通过Map集合中的方法get(key),通过key找到value Integer value = map.get(key); System.out.println(key+"="+value); } System.out.println("-------------------"); //使用增强for遍历Set集合 for(String key : map.keySet(){ //3.通过Map集合中的方法get(key),通过key找到value Integer value = map.get(key); System.out.println(key+"="+value); } }
4、Map集合遍历键值对方式(Entry) #
Map集合遍历的第二种方式:使用Entry对象遍历
Map.Entry<K,V>
:在Map接口中有一个内部接口Entry- 作用:当Map集合一创建,那么就会在Map集合中创建一个Entry对象,用来记录键与值(键值对对象,键与值的映射关系)
Map集合中的方法:
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。实现步骤:
- 1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
- 2.遍历Set集合,获取每一个Entry对象
- 3.使用Entry对象中的方法getKey()和getValue()获取键与值
public static void main(String[] args) { //创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("赵丽颖",168); map.put("杨颖",165); map.put("林志玲",178); //1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中 Set<Map.Entry<String, Integer>> set = map.entrySet(); //2.遍历Set集合,获取每一个Entry对象 //使用迭代器遍历Set集合 Iterator<Map.Entry<String, Integer>> it = set.iterator(); while(it.hasNext(){ Map.Entry<String, Integer> entry = it.next(); //3.使用Entry对象中的方法getKey()和getValue()获取键与值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); } System.out.println("-----------------------"); for(Map.Entry<String,Integer> entry:set){ //3.使用Entry对象中的方法getKey()和getValue()获取键与值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key+"="+value); } }
5、HashMap存储自定义类型键值 #
Map集合保证key是唯一的:
- 作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一。
public class Demo01HashMapSavePerson { public static void main(String[] args) { show02(); } /* HashMap存储自定义类型键值 key:Person类型 Person类就必须重写hashCode方法和equals方法,以保证key唯一 value:String类型 可以重复 */ private static void show02() { //创建HashMap集合 HashMap<Person,String> map = new HashMap<>(); //往集合中添加元素 map.put(new Person("女王",18),"英国"); map.put(new Person("秦始皇",18),"秦国"); map.put(new Person("普京",30),"俄罗斯"); map.put(new Person("女王",18),"毛里求斯"); //使用entrySet和增强for遍历Map集合 Set<Map.Entry<Person, String>> set = map.entrySet(); for (Map.Entry<Person, String> entry : set) { Person key = entry.getKey(); String value = entry.getValue(); System.out.println(key+"-->"+value); } } /* HashMap存储自定义类型键值 key:String类型 String类重写hashCode方法和equals方法,可以保证key唯一 value:Person类型 value可以重复(同名同年龄的人视为同一个) */ private static void show01() { //创建HashMap集合 HashMap<String,Person> map = new HashMap<>(); //往集合中添加元素 map.put("北京",new Person("张三",18); map.put("上海",new Person("李四",19); map.put("广州",new Person("王五",20); map.put("北京",new Person("赵六",18); //使用keySet加增强for遍历Map集合 Set<String> set = map.keySet(); for (String key : set) { Person value = map.get(key); System.out.println(key+"-->"+value); } } }
package com.anarkh.demo02.Map; 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; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } @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 hashCode() { return Objects.hash(name, 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; } }
6、LinkedHashMap集合 #
java.util.LinkedHashMap<K,V> entends HashMap<K,V>
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
底层原理:
- 哈希表+链表(记录元素的顺序)。
public class Demo01LinkedHashMap { public static void main(String[] args) { HashMap<String,String> map = new HashMap<>(); map.put("a","a"); map.put("c","c"); map.put("b","b"); map.put("a","d"); System.out.println(map);// key不允许重复,无序 {a=d, b=b, c=c} LinkedHashMap<String,String> linked = new LinkedHashMap<>(); linked.put("a","a"); linked.put("c","c"); linked.put("b","b"); linked.put("a","d"); System.out.println(linked);// key不允许重复,有序 {a=d, c=c, b=b} } }
7、Hashtable集合 #
java.util.Hashtable<K,V>集合 implements Map<K,V>接口
Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢 HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快
HashMap集合(之前学的所有的集合):可以存储null值,null键 Hashtable集合,不能存储null值,null键
Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了 Hashtable的子类Properties依然活跃在历史舞台 Properties集合是一个唯一和IO流相结合的集合
public class Demo02Hashtable { public static void main(String[] args) { HashMap<String,String> map = new HashMap<>(); map.put(null,"a"); map.put("b",null); map.put(null,null); System.out.println(map);//{null=null, b=null} Hashtable<String,String> table = new Hashtable<>(); //table.put(null,"a");//NullPointerException //table.put("b",null);//NullPointerException table.put(null,null);//NullPointerException } }
8、练习:计算一个字符串中每个字符出现次数 #
package com.anarkh.demo03.Map;
import java.util.HashMap;
import java.util.Scanner;
/*
练习:
计算一个字符串中每个字符出现次数
分析:
1.使用Scanner获取用户输入的字符串
2.创建Map集合,key是字符串中的字符,value是字符的个数
3.遍历字符串,获取每一个字符
4.使用获取到的字符,去Map集合判断key是否存在
key存在:
通过字符(key),获取value(字符个数)
value++
put(key,value)把新的value存储到Map集合中
key不存在:
put(key,1)
5.遍历Map集合,输出结果
*/
public class Demo03MapTest {
public static void main(String[] args) {
//1.使用Scanner获取用户输入的字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String str = sc.next();
//2.创建Map集合,key是字符串中的字符,value是字符的个数
HashMap<Character,Integer> map = new HashMap<>();
//3.遍历字符串,获取每一个字符
for(char c :str.toCharArray(){
//4.使用获取到的字符,去Map集合判断key是否存在
if(map.containsKey(c){
//key存在
Integer value = map.get(c);
value++;
map.put(c,value);
}else{
//key不存在
map.put(c,1);
}
}
//5.遍历Map集合,输出结果
for(Character key :map.keySet(){
Integer value = map.get(key);
System.out.println(key+"="+value);
}
}
}
9、JDK9对集合添加的优化–of方法 #
JDK9的新特性:
List接口,Set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素
static <E> List<E> of(E... elements)
- 使用前提:当集合中存储的元素的个数已经确定了,不在改变时使用
注意:
- 1.of方法只适用于List接口,Set接口,Map接口,不适用于接接口的实现类
- 2.of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常
- 3.Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常
public class Demo01JDK9 { public static void main(String[] args) { List<String> list = List.of("a", "b", "a", "c", "d"); System.out.println(list);//[a, b, a, c, d] //list.add("w");//UnsupportedOperationException:不支持操作异常 //Set<String> set = Set.of("a", "b", "a", "c", "d");//IllegalArgumentException:非法参数异常,有重复的元素 Set<String> set = Set.of("a", "b", "c", "d"); System.out.println(set); //set.add("w");//UnsupportedOperationException:不支持操作异常 //Map<String, Integer> map = Map.of("张三", 18, "李四", 19, "王五", 20,"张三",19);////IllegalArgumentException:非法参数异常,有重复的元素 Map<String, Integer> map = Map.of("张三", 18, "李四", 19, "王五", 20); System.out.println(map);//{王五=20, 李四=19, 张三=18} //map.put("赵四",30);//UnsupportedOperationException:不支持操作异常 } }
十一、斗地主案例(有序版本) #
package com.anarkh.demo06.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
/*
斗地主综合案例:有序版本
1.准备牌
2.洗牌
3.发牌
4.排序
5.看牌
*/
public class DouDiZhu {
public static void main(String[] args) {
//1.准备牌
//创建一个Map集合,存储牌的索引和组装好的牌
HashMap<Integer,String> poker = new HashMap<>();
//创建一个List集合,存储牌的索引
ArrayList<Integer> pokerIndex = new ArrayList<>();
//定义两个集合,存储花色和牌的序号
List<String> colors = List.of("♠", "♥", "♣", "♦");
List<String> numbers = List.of("2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
//把大王和小王存储到集合中
//定义一个牌的索引
int index = 0;
poker.put(index,"大王");
pokerIndex.add(index);
index++;
poker.put(index,"小王");
pokerIndex.add(index);
index++;
//循环嵌套遍历两个集合,组装52张牌,存储到集合中
for (String number : numbers) {
for (String color : colors) {
poker.put(index,color+number);
pokerIndex.add(index);
index++;
}
}
//System.out.println(poker);
//System.out.println(pokerIndex);
/*
2.洗牌
使用Collections中的方法shuffle(List)
*/
Collections.shuffle(pokerIndex);
//System.out.println(pokerIndex);
/*
3.发牌
*/
//定义4个集合,存储玩家牌的索引,和底牌的索引
ArrayList<Integer> player01 = new ArrayList<>();
ArrayList<Integer> player02 = new ArrayList<>();
ArrayList<Integer> player03 = new ArrayList<>();
ArrayList<Integer> diPai = new ArrayList<>();
//遍历存储牌索引的List集合,获取每一个牌的索引
for (int i = 0; i <pokerIndex.size() ; i++) {
Integer in = pokerIndex.get(i);
//先判断底牌
if(i>=51){
//给底牌发牌
diPai.add(in);
}else if(i%3==0){
//给玩家1发牌
player01.add(in);
}else if(i%3==1){
//给玩家2发牌
player02.add(in);
}else if(i%3==2){
//给玩家3发牌
player03.add(in);
}
}
/*
4.排序
使用Collections中的方法sort(List)
默认是升序排序
*/
Collections.sort(player01);
Collections.sort(player02);
Collections.sort(player03);
Collections.sort(diPai);
/*
5.看牌
调用看牌的方法
*/
lookPoker("刘德华",poker,player01);
lookPoker("周润发",poker,player02);
lookPoker("周星驰",poker,player03);
lookPoker("底牌",poker,diPai);
}
/*
定义一个看牌的方法,提高代码的复用性
参数:
String name:玩家名称
HashMap<Integer,String> poker:存储牌的poker集合
ArrayList<Integer> list:存储玩家和底牌的List集合
查表法:
遍历玩家或者底牌集合,获取牌的索引
使用牌的索引,去Map集合中,找到对应的牌
*/
public static void lookPoker(String name,HashMap<Integer,String> poker,ArrayList<Integer> list){
//输出玩家名称,不换行
System.out.print(name+":");
//遍历玩家或者底牌集合,获取牌的索引
for (Integer key : list) {
//使用牌的索引,去Map集合中,找到对应的牌
String value = poker.get(key);
System.out.print(value+" ");
}
System.out.println();//打印完每一个玩家的牌,换行
}
}