JavaSE-Java集合

推荐先阅读JavaSE

Set

Set系列集合:添加的元素是无序(底层采用了哈希表存储元素),不重复,无索引的。
无序,不可重复,最多允许有一个Null元素对象,取元素时只能用Iterator接口取得所有元 素,在逐一遍历各个元素。元素在set中的位置是有该元素的 HashCode 决定的,其位置其实是固定的(加入Set 的 Object 必须定义 equals ()方法)。
迭代方法:迭代器 foreach lambda
查询慢,增删快(因为增删不会引起元素位置改变 )

  • HashSet:添加的元素是无序,不重复,无索引的。
  • LinkedHashSet:添加的元素是有序,不重复,无索引的。
  • TreeSet:不重复,无索引,按照大小默认升序排序!!

JDK 1.8之前:哈希表 = 数组 + 链表 + (哈希算法)
JDK 1.8之后:哈希表 = 数组 + 链表 + 红黑树 + (哈希算法)
当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
Set系列集合是基于哈希表存储数据的
它的增删改查的性能都很好!!但是它是无序不重复的!如果不在意当然可以使用!

List

List系列集合:添加的元素是有序,可重复,有索引。
有序,按对象进入的顺序保存对象,可重复,允许多个Null元素对象,可以使用Iterator取出所有元素,在逐一遍历,还可以使用get(int index)获取指定下标的元素。
迭代方法:迭代器 foreach lambda fori
查询快(通过下标),增删慢(因为会引起其他元素位置改变)

  • ArrayList:基于数组存储数据的,添加的元素是有序,可重复,有索引。查询快,增删慢。
  • LinekdList:添加的元素是有序,可重复,有索引。

List集合继承了Collection集合的全部功能.

  • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
  • public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回更新前的元素值。

API

  • public boolean add(E e): 把给定的对象添加到当前集合中 。
  • public void clear() :清空集合中所有的元素。
  • public boolean remove(E e): 把给定的对象在当前集合中删除。
  • public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
  • public boolean isEmpty(): 判断当前集合是否为空。
  • public int size(): 返回集合中元素的个数。
  • public Object[] toArray(): 把集合中的元素,存储到数组中

集合的遍历

  • 迭代器

    Iterator<String> it = lists.iterator();
    while(it.hasNext()){
    String ele = it.next();
    System.out.println(ele);
    }
  • foreach

    for (String ele : lists) {
    System.out.println(ele);
    }
  • lambda

    lists.forEach(s -> System.out.println(s));

List集合遍历比Collection集合多一个fori循环

for(int i = 0 ; i < lists.size() ; i++ ) {
String ele = lists.get(i);
}

常见的数据结构种类

a.队列(queue)

  • 先进先出,后进后出。
  • 场景:各种排队。叫号系统。
  • 有很多集合可以实现队列。

b.栈(stack)

  • 后进先出,先进后出
  • 压栈 == 入栈
  • 弹栈 == 出栈
  • 场景:手枪的弹夹。

c.数组

  • 数组是内存中的连续存储区域。
  • 分成若干等分的小区域(每个区域大小是一样的)
  • 元素存在索引
  • 特点:查询元素快(根据索引快速计算出元素的地址,然后立即去定位)
    增删元素慢(创建新数组,迁移元素)

d.链表

  • 元素不是内存中的连续区域存储。
  • 元素是游离存储的。每个元素会记录下个元素的地址。
  • 特点:查询元素慢
    增删元素快(针对于首尾元素,速度极快,一般是双链表)

e.红黑树
二叉树:binary tree 永远只有一个根节点,是每个结点不超过2个节点的树(tree) 。
查找二叉树,排序二叉树:小的左边,大的右边,但是可能树很高,性能变差。
为了做排序和搜索会进行左旋和右旋实现平衡查找二叉树,让树的高度差不大于1

红黑树(就是基于红黑规则实现了自平衡的排序二叉树):
树尽量的保证到了很矮小,但是又排好序了,性能最高的树。
红黑树的增删查改性能都好!!!

LinkedList

LinkedList: 基于链表,增删比较快,查询慢!!
LinkedList是支持双链表,定位前后的元素是非常快的,增删首尾的元素也是最快的
所以提供了很多操作首尾元素的特殊API可以做栈和队列的实现。

  • public void addFirst(E e):将指定元素插入此列表的开头。
  • public void addLast(E e):将指定元素添加到此列表的结尾。
  • public E getFirst():返回此列表的第一个元素。
  • public E getLast():返回此列表的最后一个元素。
  • public E removeFirst():移除并返回此列表的第一个元素。
  • public E removeLast():移除并返回此列表的最后一个元素。
  • public E pop():从此列表所表示的堆栈处弹出一个元素。
  • public void push(E e):将元素推入此列表所表示的堆栈。

用LinkedList做一个队列:先进先出,后进后出。

LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("1号");
queue.addLast("2号");
queue.addLast("3号");
queue.addLast("4号");
System.out.println(queue); // [1号, 2号, 3号, 4号]
// 出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);

做一个栈

LinkedList<String> stack = new LinkedList<>();
// 压栈
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack); // [第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]
// 弹栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);

LinkedHashSet

LinkedHashSet: HashSet的子类,元素是“有序” 不重复,无索引.
LinkedHashSet底层依然是使用哈希表存储元素的,
但是每个元素都额外带一个链来维护添加顺序!!
不光增删查快,还有序。
缺点是多了一个存储顺序的链会占内存空间!!而且不允许重复,无索引。

集合的使用场景

如果希望元素可以重复,又有索引,查询要快用ArrayList集合。(用的最多)
如果希望元素可以重复,又有索引,增删要快要用LinkedList集合。(适合查询元素比较少的情况,经常要首尾操作元素的情况)
如果希望增删改查都很快,但是元素不重复以及无序无索引,那么用HashSet集合。
如果希望增删改查都很快且有序,但是元素不重复以及无索引,那么用LinkedHashSet集合。

TreeSet

TreeSet: 不重复,无索引,按照大小默认升序排序!!
TreeSet集合称为排序不重复集合,可以对元素进行默认的升序排序。

TreeSet集合自自排序的方式:
1.有值特性的元素直接可以升序排序。(浮点型,整型)
2.字符串类型的元素会按照首字符的编号排序。
3.对于自定义的引用数据类型,TreeSet默认无法排序,执行的时候直接报错,因为人家不知道排序规则。

Collection排序

注意:如果类和集合都存在大小规则,默认使用集合自带的规则进行大小排序!!

a. 类实现Comparable接口

public class Orange implements Comparable {
private String name;
private double weight;
private String price;
@Override
public String toString() {
return "Orange{" +
"name='" + name + '\'' +
", weight=" + weight +
", price='" + price + '\'' +
'}'+"\n";
}
@Override
public int compareTo(Object o) {
Orange o2 = (Orange) o;
// 集合会自动送入两个句子对象来比较!!
// 认为o1 > o2返回正整数。
// 认为o1 = o2返回0。
// 认为o1 < o2返回负整数。
if(this.weight > o2.weight) return 1;
if(this.weight < o2.weight) return -1;
return 0;
}
}

b. TreeSet直接为设置比较器Comparator对象,重写比较方法

public TreeSet(Comparator<? super E> comparator)

Set<Employee> employees1 = new TreeSet<>(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return o1.getAge() - o2.getAge();
}
});
employees1.add(new Employee("播仔",6500.0,21));
employees1.add(new Employee("播妞",7500.0,19));
employees1.add(new Employee("乔治",4500.0,23));
System.out.println(employees1);

Collections集合的方法

List<Orange> oranges1 = new ArrayList<>();
Orange o11 = new Orange("红橘子",654.0 ,"贼便宜~");
Orange o22 = new Orange("黄橘子",454.0 ,"贼便宜~");
Orange o33 = new Orange("黄橘子",454.0 ,"贼便宜~");
Orange o44 = new Orange("青橘子",456.0 ,"贼便宜~");
Collections.addAll(oranges1,o11,o22,o33,o44);
// 优先用方法自带的比较器对象Comparator而不会用类的比较规则!!
Collections.sort(oranges1, new Comparator<Orange>() {
@Override
public int compare(Orange o1, Orange o2) {
if(o1.getWeight() > o2.getWeight()) return -1;
if(o1.getWeight() < o2.getWeight()) return 1;
return 0;
}
});
System.out.println(oranges1);

Collections Util

  • public static <T> boolean addAll(Collection<? super T> c, T... elements):给集合对象批量添加元素!
  • public static void shuffle(List<?> list) :打乱集合顺序。
  • public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
  • public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。

args

可变参数的格式:数据类型… 参数名称
可变参数在方法内部本质上就是一个数组。

可变参数的注意事项:
1.一个形参列表中可变参数只能有一个!!
2.可变参数必须放在形参列表的最后面!!

public class MethodDemo {
public static void main(String[] args) {
sum(); // 可以不传输参数。
sum(10); // 可以传输一个参数。
sum(10,20,30); // 可以传输多个参数。
sum(new int[]{10,30,50,70,90}); // 可以传输一个数组。
}
public static void sum(int...nums){
// 可变参数在方法内部本质上就是一个数组。
System.out.println("元素个数:"+nums.length);
System.out.println("元素内容:"+ Arrays.toString(nums));
System.out.println("--------------------------");
}
}

斗地主游戏的案例开发

/**
目标:斗地主游戏的案例开发。

业务需求分析:
斗地主的做牌,洗牌,发牌,排序(拓展知识), 看牌
业务:总共有54张牌。
点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
花色: "♠", "♥", "♣", "♦"
大小王: "👲" , ""
点数分别要组合4种花色,大小王各一张。
斗地主:发出51张牌,剩下3张作为底牌。

功能:
1.做牌。
2.洗牌
3.定义3个玩家。
4.发牌。
5.排序(拓展,了解)
6.看牌。

用面向对象设计案例:
a.定义一个牌类,代表牌对象。 一个牌对象代表一张牌。
b.定义一个集合存储54张牌,集合只需要一个(因为牌只需要一副)
*/
public class GameDemo {
/** a.定义一个静态集合,存储54张牌对象,集合只需要一个 */
public static final List<Card> ALL_CARDS = new ArrayList<>();

/** b.做牌 */
static {
// 1.定义一个数组存储牌的点数,类型确定,个数确定请用数组存储!
String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
// 2.定义一个数组存储牌的花色,类型确定,个数确定请用数组存储!
String[] colors = { "♠", "♥", "♣", "♦" };
// 3.先遍历点数与四种花色组装成牌对象存入到集合中去
for (String number : numbers) {
// 遍历花色
for (String color : colors) {
// 创建一张牌对象封装点数和花色
Card card = new Card(number , color);
ALL_CARDS.add(card);
}
}
// 4.单独加入大小王
Collections.addAll(ALL_CARDS , new Card("","🃏") ,new Card("","👲") );
System.out.println("输出新牌:"+ALL_CARDS);
}

public static void main(String[] args) {

}
}

// 牌类
public class Card {
private String number;
private String color;

@Override
public String toString() {
return number+color;
}
}

Map

Map集合的体系:
Map<K , V>(接口,Map集合的祖宗类)
/ \
TreeMap<K , V> HashMap<K , V>(实现类,经典的,用的最多)
\
LinkedHashMap<K, V>(实现类)
Map集合是另一个集合体系。
Collection是单值集合体系。

Map集合是一种双列集合,每个元素包含两个值。
Map集合的每个元素的格式:key=value(键值对元素)。
Map集合也被称为“键值对集合”。

Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , ...}

Map集合有啥用?
1.Map集合存储的信息更加的具体丰富。
Collection: ["苍老师","日本","女","动作演员",23,"广州"]
Map : {name="苍老师" , jiaxiang=小日本 , sex="女" , age = 23 , addr=广州}

2.Map集合很适合做购物车这样的系统。
Map: {娃娃=30 , huawei=1 , iphonex=1}

注意:集合和泛型都只能支持引用数据类型,集合完全可以称为是对象容器,存储都是对象。

Map集合的特点:
1.Map集合的特点都是由键决定的。
2.Map集合的键是无序,不重复的,无索引的。
Map集合后面重复的键对应的元素会覆盖前面的整个元素!
3.Map集合的值无要求。
4.Map集合的键值对都可以为null

HashMap:元素按照键是无序,不重复,无索引,值不做要求。
LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。
// 一行经典代码
maps.put("娃娃",1);
maps.put("huawei",10);
maps.put("iphoneXS",2);
maps.put(null , null );
maps.put("娃娃",30);
maps.put("特仑苏",2);
System.out.println(maps);

API

- V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
- Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
- Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
- boolean containKey(Object key):判断该集合中是否有此键。
- boolean containsValue(Object value);
- void clear();清空集合
- Collection<V> values();获取全部值的集合 Map集合的值是不做要求的,可能重复,所以值要用Collection集合接收!
- void putAll(Map<? extends K, ? extends V> m);
// 添加元素: 无序,不重复,无索引。
Map<String , Integer> maps = new HashMap<>();
maps.put("iphoneX",10);
maps.put("iphoneX",100);// Map集合后面重复的键对应的元素会覆盖前面重复的整个元素!

遍历方式

a.“键找值”

Set<String> keys = maps.keySet();
for (String key : keys) {
Integer value = maps.get(key);
}

b.“键值对”

Set<Map.Entry<String,Integer>> entries = maps.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
String key = entry.getKey();
Integer value = entry.getValue();
}

c.Lambda表达式

maps.forEach((k , v) -> System.out.println(k+"==>"+v));

存储自定义类型

Map集合的键和值都可以存储自定义类型。
如果希望Map集合认为自定义类型的键对象重复了,必须重写对象的hashCode()和equals()方法
Map<Orange,String> maps = new HashMap<>();
Orange o1 = new Orange("黄橘子",20.3 , "贼便宜!");
maps.put(o1 , "江西\n");
maps.put(o2 , "赣州\n");
maps.put(o3 , "广州\n");
maps.put(o4 , "广西\n");

public class Orange {
private String name;
private double weight;
private String price;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Orange orange = (Orange) o;
return Double.compare(orange.weight, weight) == 0 &&
Objects.equals(name, orange.name) &&
Objects.equals(price, orange.price);
}
@Override
public int hashCode() {
return Objects.hash(name, weight, price);
}
public String toString() {
return "Orange{name = " + name + ", weight = " + weight + ", price = " + price + "}";
}
}

LinkedHashMap

HashMap 无序不重复
LinkedHashMap 有序不重复
他们都是基于哈希表存储数据,增删改查都很好。

HashSet集合相当于是HashMap集合的键都不带值。
LinkedHashSet集合相当于是LinkedHashMap集合的键都不带值。

TreeMap

TreeMap集合按照键是可排序不重复的键值对集合。(默认升序)
TreeMap集合和TreeSet集合都是排序不重复集合

TreeSet集合的底层是基于TreeMap,只是键没有附属值而已。
所以TreeMap集合指定大小规则有2种方式:
a.直接为对象的类实现比较器规则接口Comparable,重写比较方法(拓展方式)
b.直接为集合设置比较器Comparator对象,重写比较方法

Properties

-- public Object setProperty(String key, String value) : 保存一对属性。
-- public String getProperty(String key) :使用此属性列表中指定的键搜索属性值
-- public Set<String> stringPropertyNames() :所有键的名称的集合
-- public void store(OutputStream out, String comments):保存数据到属性文件中去
-- public void store(Writer fw, String comments):保存数据到属性文件中去
-- public synchronized void load(InputStream inStream):加载属性文件的数据到属性集对象中去
-- public synchronized void load(Reader fr):加载属性文件的数据到属性集对象中去

properties.load(new FileInputStream("Day10Demo/src/users.properties"));
System.out.println(properties);
System.out.println(properties.getProperty("dlei"));
System.out.println(properties.getProperty("admin"));

输出一个字符串中每个字符出现的次数。(经典面试题)

package com.zx._07Map集合练习;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
目标:输出一个字符串中每个字符出现的次数。(经典面试题)

分析:
(1)键盘录入一个字符串。aabbccddaa123。
(2)定义一个Map集合,键是每个字符,值是其出现的次数。 {a=4 , b=2 ,...}
(3)遍历字符串中的每一个字符。
(4)拿着这个字符去Map集合中看是否有这个字符键,有说明之前统计过,其值+1
没有这个字符键,说明该字符是第一次统计,直接存入“该字符=1”

*/
public class MapDemo01 {
public static void main(String[] args) {
// (1)键盘录入一个字符串。aabbccddaa123。
Scanner scanner = new Scanner(System.in);
System.out.print("请您输入一个字符串:");
String datas = scanner.nextLine();

// (2)定义一个Map集合,键是每个字符,值是其出现的次数。
Map<Character , Integer> infos = new HashMap<>(); // {a=2,b=2}

// (3)遍历字符串中的每一个字符。
// datas = aabbccddaa123
for(int i = 0 ; i < datas.length() ; i++ ){
// 取出当前索引的字符
char ch = datas.charAt(i);
// (4)拿着这个字符去Map集合中看是否有这个字符键,有说明之前统计过,其值+1
// 没有这个字符键,说明该字符是第一次统计,直接存入“该字符=1”
if(infos.containsKey(ch)){
// 说明这个字符之前已经出现过,其值+1
infos.put(ch , infos.get(ch) + 1);
}else{
// 说明这个字符第一次出现,直接存入 a=1
infos.put(ch , 1);
}
}
// (5)输出结果
System.out.println("结果:"+infos);

}
}

斗地主游戏的案例开发-Map集合实现。

package com.zx._08斗地主游戏洗牌发牌看牌;

import java.util.*;

/**
目标:斗地主游戏的案例开发-Map集合实现。

业务需求分析:
斗地主的做牌,洗牌,发牌,排序(拓展知识), 看牌
业务:总共有54张牌。
点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
花色: "♠", "♥", "♣", "♦"
大小王: "👲" , "🃏"
点数分别要组合4种花色,大小王各一张。
斗地主:发出51张牌,剩下3张作为底牌。

功能:
1.做牌。
2.洗牌
3.定义3个玩家。
4.发牌。
5.
6.看牌。

用面向对象设计案例:
a.定义一个牌类,代表牌对象。 一个牌对象代表一张牌。
b.定义一个集合存储54张牌,集合只需要一个(因为牌只需要一副)
{card1=0 , card2=1, ......}
*/
public class GameDemo {
/** 1.定义一个Map集合存储54张牌对象,键是牌对象,值是其大小 用于实现大小排序 */
public static final Map<Card , Integer> ALL_CARDS_SIZE = new HashMap<>();
// 真正存储54张牌对象的List集合,用于做牌,洗牌,发牌!
public static final List<Card> ALL_CARDS = new ArrayList<>();
/** 2.做牌:静态代码块初始化54张牌对象 */
static {
// 1.定义一个数组存储牌的点数,类型确定,个数确定请用数组存储!
String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
// 2.定义一个数组存储牌的花色,类型确定,个数确定请用数组存储!
String[] colors = { "♠", "♥", "♣", "♦" };

// 3.先遍历点数与四种花色组装成牌对象存入到集合中去
int index = 0;
for (String number : numbers) {
// 遍历花色
for (String color : colors) {
// 创建一张牌对象封装点数和花色
Card card = new Card(number , color);
ALL_CARDS.add(card);
ALL_CARDS_SIZE.put(card , index++);
}
}
Card c1 = new Card("","🃏");
Card c2 = new Card("", "👲");

ALL_CARDS.add(c1);
ALL_CARDS.add(c2);
ALL_CARDS_SIZE.put(c1 , index++);
ALL_CARDS_SIZE.put(c2 , index++);
System.out.println("新牌:"+ ALL_CARDS);
}

public static void main(String[] args) {
/**
c.洗牌(把新牌的牌顺序打乱)
*/
Collections.shuffle(ALL_CARDS);
System.out.println("洗牌后:"+ALL_CARDS);

/**
d.定义3个玩家。
*/
List<Card> lingHuChong = new ArrayList<>();
List<Card> jiuMoZhi = new ArrayList<>();
List<Card> dongfangbubai = new ArrayList<>();

/**
e.发牌 依次发出51张牌出去。
ALL_CARDS = [J♠, 5♠, 2♠, 9♣, 5♦, 4♠, Q♣, 6♥, 8♠, ......
0 1 2 3 4 5 6 7 8 % 3(轮询的长度)
*/
for(int i = 0 ; i < ALL_CARDS.size() - 3 ; i++ ){
// 得到当前这张牌对象
Card c = ALL_CARDS.get(i);
// 判断这个牌发给谁
if(i % 3 == 0 ){
// 请令狐冲接牌
lingHuChong.add(c);
}else if(i % 3 == 1){
// 请啊鸠接牌
jiuMoZhi.add(c);
}else if(i % 3 == 2){
// 请阿东接牌
dongfangbubai.add(c);
}
}

/**
* f.对牌进行排序(拓展,了解)
*/
sortCards(lingHuChong);
sortCards(jiuMoZhi);
sortCards(dongfangbubai);

/**
g.看牌
*/
System.out.println("令狐冲:"+lingHuChong);
System.out.println("鸠摩智:"+jiuMoZhi);
System.out.println("东方不败:"+dongfangbubai);
//System.out.println("底牌:"+ALL_CARDS.get(53) +"-"+ALL_CARDS.get(52) + "-" +ALL_CARDS.get(51) );
// 拓展: 截取集合的最后三张牌到一个新的List集合中去。
List<Card> lastThreeCards = ALL_CARDS.subList(ALL_CARDS.size() -3 , ALL_CARDS.size());
System.out.println("底牌:"+lastThreeCards);

}

/**
对牌的List集合进行排序(降序排序)
*/
private static void sortCards(List<Card> cards) {
// cards = [ 3♦ , 👲, 10♣ , 9♦ , 8♦ , J♠ , ...
Collections.sort(cards, new Comparator<Card>() {
@Override
public int compare(Card o1, Card o2) {
// o1 Q♦
// o2 👲
// 牌的大小就是当前牌在Map集合中的值存储的大小!
return ALL_CARDS_SIZE.get(o2) - ALL_CARDS_SIZE.get(o1);
}
});
}
}

图书管理系统的开发

package com.zx._09图书管理案例;

import java.util.*;

/**
目标:图书管理系统的开发。

业务需求分析:
(1)查看全部书籍。query
(2)添加书本信息。add
(3)删除书本信息。delete
(4)修改书本信息。update
(5)退出系统。 exit

书本信息的结构:
类型 书名 价格 作者
---------------------------------------------------
言情小说
《金瓶梅》 99.9 阿猫
《红楼梦》 198.2 曹雪芹
武侠小说
《三少爷的剑》 98.2 古龙
《神雕侠侣》 98.2 金庸
------------------------------------------------------
分析:
(1)定义一个书本类。
(2)定义一个集合表示图书馆用于存储书本信息数据:Map<String,List<Book>>。
(3)展示操作界面。
(4)完成相应功能。
*/
public class BookSystem {
/**
1.定义一个集合表示图书馆用于存储书本信息数据:Map<String,List<Book>>。
使用Map集合,键是橱柜的栏目类型名称,值是橱柜的List集合对象存储书本信息
*/
public static final Map<String, List<Book>> BOOK_STORE = new HashMap<>();
public static final Scanner SYS_SCANNER = new Scanner(System.in);

public static void main(String[] args) {
/** 2.展示操作界面 :独立功能建立封装成方法调用 方便互相之间的逻辑调用 */
showCommand();
}

/**
* 展示操作界面的命令
*/
private static void showCommand() {
System.out.println("===============欢迎您进入系统===================");
System.out.println("(1)查看全部书籍。query");
System.out.println("(2)添加书本信息。add");
System.out.println("(3)删除书本信息。delete");
System.out.println("(4)修改书本信息。update");
System.out.println("(5)退出系统。 exit");
System.out.print("请您输入您的操作命令:");
String command = SYS_SCANNER.nextLine();
// 判断用户的命令是想干啥
switch (command){
case "query":
// 查看全部书籍
queryBooks();
break;
case "add":
// 添加书籍
addBook();
break;
case "delete":
// 删除书籍
break;
case "update":
// 修改书籍
updateBook();
break;
case "exit":
// 退出系统
System.out.println("退出成功,期待您下次光临!");
System.exit(0);
break;
default:
System.err.println("您的命令输入有无,请重新确认!");
}
showCommand(); // 调用自己
}

/**
修改书本信息。
*/
private static void updateBook() {
if(BOOK_STORE.size() == 0 ){
System.out.println("您现在根本没有任何栏目可以修改!");
}else{
queryBooks();
System.out.println("===============欢迎您进入修改书本业务=================");
while(true){
System.out.print("请您输入修改书本的栏目:");
String type = SYS_SCANNER.nextLine();
// 1.判断是否存在该栏目
if(BOOK_STORE.containsKey(type)){
while(true){
// 存在该栏目
// 2.请输入要修改的书名
System.out.print("请您输入修改书本的名称:");
String name = SYS_SCANNER.nextLine();
// 3.判断该栏目下是否存在该书本对象。
// 根据栏目和书名去栏目下查询出这本书对象
Book book = getBookByTypeAndName(type , name);
if(book == null){
System.err.println("您的输入的书名不存在,,请重新确认!");
}else{
// 书名正确了,开始正式修改
System.out.println("请您输入修改书本的新名称:");
String newName = SYS_SCANNER.nextLine();
System.out.println("请您输入修改书本的新价格:");
String newPrice = SYS_SCANNER.nextLine();
System.out.println("请您输入修改书本的新作者:");
String newAuthor = SYS_SCANNER.nextLine();
book.setName(newName);
book.setPrice(Double.valueOf(newPrice));
book.setAuthor(newAuthor);
queryBooks();
System.out.println("您修改的书本成功,请看如上信息确认!");
return; // 结束修改的方法!
}
}
}else{
// 不存在该栏目
System.err.println("您输入的栏目不存在,请重新确认!");
}
}
}

}

/**
* 在某个栏目下,根据书名查询出这本书对象返回!
* @param type 栏目名称
* @param name 书名称
* @return 书本对象
*/
public static Book getBookByTypeAndName(String type , String name){
// 1.先提取这个栏目的橱柜,根据栏目提取它的值
List<Book> books = BOOK_STORE.get(type);
for (Book book : books) {
// 如果本书的名称与要找的名称一样,该书就是我们要的,直接返回!
if(book.getName().equals(name)){
return book;
}
}
return null;// 没有这本书
}

/**
查询全部书本信息
Map<String, List<Book>> BOOK_STORE = {type1=[b1, b2, b3] , type2=[b1] }
*/
private static void queryBooks() {
System.out.println("===============欢迎您进入查询书本业务=================");
if(BOOK_STORE.size() == 0){
System.out.println("您的图书馆一本书都没有,请赶紧买书去!");
}else{
System.out.println("类型\t\t\t\t书名\t\t\t\t\t价格\t\t\t作者");
BOOK_STORE.forEach((type , books) -> {
System.out.println(type);
// 遍历该类型下的橱柜中的全部书本对象
for (Book book : books) {
System.out.println("\t\t\t\t"+book.getName()+"\t\t\t"+book.getPrice()+"\t\t"+book.getAuthor());
}
});
}
}

/**
添加书本信息数据
Map<String, List<Book>> BOOK_STORE = {type1=[] , }
*/
private static void addBook() {
System.out.println("===============欢迎您进入添加书本业务=================");
System.out.print("请您输入添加书本的栏目:");
String type = SYS_SCANNER.nextLine();
// 定义一个List集合用于指向栏目的橱柜
// 这个橱柜变量要根据栏目是否存在来确定是用已经存在的橱柜,还是创建一个新橱柜!
List<Book> books = null ;
// 1.判断是否存在该栏目。
if(BOOK_STORE.containsKey(type)) {
// 3.存在该栏目
// 直接得到该栏目存在的橱柜对象
books = BOOK_STORE.get(type);
}else{
// 2.该栏目是第一次添加。
// 为该栏目创建一个橱柜对象(List<Book>)
books = new ArrayList<>();
// 新橱柜和新栏目必须手工加入到图书馆!
BOOK_STORE.put(type , books);
}
System.out.println("请您输入添加书本的名称:");
String name = SYS_SCANNER.nextLine();
System.out.println("请您输入添加书本的价格:");
String price = SYS_SCANNER.nextLine();
System.out.println("请您输入添加书本的作者:");
String author = SYS_SCANNER.nextLine();
// 定义一个书本对象封装用户输入的书本信息。
Book book = new Book(name , Double.valueOf(price) , author);
books.add(book);
System.out.println("您添加在"+type+"下的书本"+book.getName()+"成功!");
}
}