Java集合概述与典例分析

一. Java集合分类

Collection单列集合,每个元素(数据)只包含一个值

Map双列集合,每个元素包含两个值(键值对)

 1. Collection集合

List系列集合:添加的元素是有序可重复有索引。e.g. ArrayList、LinekdList

Set系列集合:添加的元素是无序不重复无索引。e.g. HashSet、TreeSet、LinkedHashSet

公有方法:

  • 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()                     把集合中的元素,存储到数组中

 2. Map集合

HashMap:元素按照键是无序不重复无索引,值不做要求。

LinkedHashMap:元素按照键是有序不重复无索引,值不做要求。

TreeMap:元素按照建是排序不重复无索引,值不做要求。

公有方法:

  • V put(K key,V value)                                添加元素
  • V remove(Object key)                             根据键删除键值对元素
  • void clear()                                              移除所有的键值对元素
  • boolean containsKey(Object key)           判断集合是否包含指定的键
  • boolean containsValue(Object value)     判断集合是否包含指定的值
  • boolean isEmpty()                                   判断集合是否为空
  • int size()                                                  集合的长度,也就是集合中键值对的个数

 二. Java集合遍历

方式一:迭代器

    ArrayList<Integer> al = new ArrayList<>();
    Iterator<Integer> it = al.iterator();
    /** 方式一 迭代器循环 */
        while (it.hasNext()) {
            int item = it.next();
            /** Integer item = it.next(); is also ok! */
            System.out.println(item);
           }

 方式二:foreach/增强for循环

    for (int item : al) {
         System.out.println(item);
        }

方式三:lambda表达式

    al.forEach(s -> {
         System.out.println(s);
         });

三. Java集合运用

1.自创Java集合巧解问题

问题描述:

       假定SZU有N(N>10^5)盏灯,对应N个开关,编号为1~N。每按下一次开关,都会将其控制的灯的编号返回给总控电脑。总控电脑会依次记录返回的编号,并统计总记录数。为节约电能,总控处需要在每晚23点统计没有关闭的灯的数量,并按灯自上次点亮到统计时的亮灯时长降序排列输出。请编写程序实现统计功能。

输入:

        记录数n(n>10^6),记录的n个编号K1,K2,...,Kn.

输出:

        未关闭灯数量,按题干规则输出未关闭灯的编号。

样例输入:

        10

        1 23 546 3 23 5 4 1 9 1

样例输出:

        6

        546 3 5 4 9 1

分析:

由于涉及到有序输出,元素不重复,故选择LinkedHashSet较合适。

代码实现:

import java.util.LinkedHashSet;
import java.util.Scanner;

/** @author: LinuxMumu */
public class PowerPointTest {
    public static void main(String[] args) {
        LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
        Scanner sc = new Scanner(System.in);
        int n, lightIndex;
        n = sc.nextInt();
        for (int i = 0; i < n; i++) {
            lightIndex = sc.nextInt();
            if (!lhs.add(lightIndex)) {
                lhs.remove(lightIndex);
            }
        }
        System.out.println(lhs.size());
        for (int item : lhs) {
            System.out.print(item);
            System.out.print(" ");
        }
        System.out.println();
        sc.close();
    }
}

2. CSP201703-2 《学生排队》

问题描述

体育老师小明要将自己班上的学生按顺序排队。他首先让学生按学号从小到大的顺序排成一排,学号小的排在前面,然后进行多次调整。一次调整小明可能让一位同学出队,向前或者向后移动一段距离后再插入队列。

例如,下面给出了一组移动的例子,例子中学生的人数为8人。

0)初始队列中学生的学号依次为1, 2, 3, 4, 5, 6, 7, 8;

1)第一次调整,命令为“3号同学向后移动2”,表示3号同学出队,向后移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 3, 6, 7, 8;

2)第二次调整,命令为“8号同学向前移动3”,表示8号同学出队,向前移动3名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 8, 3, 6, 7;

3)第三次调整,命令为“3号同学向前移动2”,表示3号同学出队,向前移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 3, 5, 8, 6, 7。

小明记录了所有调整的过程,请问,最终从前向后所有学生的学号依次是多少?

请特别注意,上述移动过程中所涉及的号码指的是学号,而不是在队伍中的位置。在向后移动时,移动的距离不超过对应同学后面的人数,如果向后移动的距离正好等于对应同学后面的人数则该同学会移动到队列的最后面。在向前移动时,移动的距离不超过对应同学前面的人数,如果向前移动的距离正好等于对应同学前面的人数则该同学会移动到队列的最前面。

输入格式

输入的第一行包含一个整数n,表示学生的数量,学生的学号由1到n编号。

第二行包含一个整数m,表示调整的次数。

接下来m行,每行两个整数p, q,如果q为正,表示学号为p的同学向后移动q,如果q为负,表示学号为p的同学向前移动-q。

输出格式

输出一行,包含n个整数,相邻两个整数之间由一个空格分隔,表示最终从前向后所有学生的学号。

样例输入

8

3

3 2

8 -3

3 -2

样例输出

1 2 4 3 5 8 6 7

评测用例规模与约定

对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 1000,所有移动均合法。

分析:

分析题干易知,需要对一列数据进行多次前移或后移操作,最后输出操作该列数据。对该问题采取ArrayList集合进行存储数据, 对每次操作,通过删除原有值,并在适当位置插入原有值来实现。先保存1~n到list集合中,再读取每次操作数据的值和操作。对每次操作,先通过indexOf方法找到要操作的数据的下表index,再通过计算操作确定插入点insertIndex。最后,删除原有下表处的值,并在insertIndex处插入当次操作数据的值即可。

代码实现:

import java.util.ArrayList;
import java.util.Scanner;

/** @author: Linux_Mumu */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int num, op, index, insertIndex;
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            list.add(i + 1);
        }
        for (int i = 0; i < m; i++) {
            num = sc.nextInt();
            op = sc.nextInt();
            index = list.indexOf(num);
            insertIndex = Math.min(index + op, list.size());
            insertIndex = Math.max(insertIndex, 0);
            list.remove(index);
            list.add(insertIndex, num);
        }
        for (int item : list) {
            System.out.print(item);
            System.out.print(" ");
        }
        System.out.println();
        sc.close();
    }
}

3. CSP201609-3  《炉石传说》

问题描述

《炉石传说:魔兽英雄传》(Hearthstone: Heroes of Warcraft,简称炉石传说)是暴雪娱乐开发的一款集换式卡牌游戏。游戏在一个战斗棋盘上进行,由两名玩家轮流进行操作,本题所使用的炉石传说游戏的简化规则如下:

 

* 玩家会控制一些角色(Role),每个角色有自己的生命值和攻击力。当生命值小于等于 0 时,该角色死亡。角色分为英雄和随从。

* 玩家各控制一个英雄(Hero),游戏开始时,英雄的生命值为 30,攻击力为 0。当英雄死亡时,游戏结束,英雄未死亡的一方获胜。

* 玩家可在游戏过程中召唤随从(Servant)。棋盘上每方都有 7 个可用于放置随从的空位,从左到右一字排开,被称为战场。当随从死亡时,它将被从战场上移除。

* 游戏开始后,两位玩家轮流进行操作(Command),每个玩家的连续一组操作称为一个回合(Turn)。

* 每个回合中,当前玩家可进行零个或者多个以下操作:

1) 召唤随从:玩家召唤一个随从进入战场,随从具有指定的生命值和攻击力。

2) 随从攻击:玩家控制自己的某个随从攻击对手的英雄或者某个随从。

3) 结束回合:玩家声明自己的当前回合结束,游戏将进入对手的回合。该操作一定是一个回合的最后一个操作。

* 当随从攻击时,攻击方和被攻击方会同时对彼此造成等同于自己攻击力的伤害。受到伤害的角色的生命值将会减少,数值等同于受到的伤害。例如,随从 X 的生命值为 HX、攻击力为 AX,随从 Y 的生命值为 HY、攻击力为 AY,如果随从 X 攻击随从 Y,则攻击发生后随从 X 的生命值变为 HX - AY,随从 Y 的生命值变为 HY - AX。攻击发生后,角色的生命值可以为负数。

本题将给出一个游戏的过程,要求编写程序模拟该游戏过程并输出最后的局面。

输入格式

输入第一行是一个整数 n,表示操作的个数。接下来 n 行,每行描述一个操作,格式如下:

<action> <arg1> <arg2> ...

其中<action>表示操作类型,是一个字符串,共有 3 种:summon表示召唤随从,attack表示随从攻击,end表示结束回合。这 3 种操作的具体格式如下:

* summon <position> <attack> <health>:当前玩家在位置<position>召唤一个生命值为<health>、攻击力为<attack>的随从。其中<position>是一个 1 到 7 的整数,表示召唤的随从出现在战场上的位置,原来该位置及右边的随从都将顺次向右移动一位。

* attack <attacker> <defender>:当前玩家的角色<attacker>攻击对方的角色 <defender>。<attacker>是 1 到 7 的整数,表示发起攻击的本方随从编号,<defender>是 0 到 7 的整数,表示被攻击的对方角色,0 表示攻击对方英雄,1 到 7 表示攻击对方随从的编号。

* end:当前玩家结束本回合。

注意:随从的编号会随着游戏的进程发生变化,当召唤一个随从时,玩家指定召唤该随从放入战场的位置,此时,原来该位置及右边的所有随从编号都会增加 1。而当一个随从死亡时,它右边的所有随从编号都会减少 1。任意时刻,战场上的随从总是从1开始连续编号。

输出格式

输出共 5 行。

第 1 行包含一个整数,表示这 n 次操作后(以下称为 T 时刻)游戏的胜负结果,1 表示先手玩家获胜,-1 表示后手玩家获胜,0 表示游戏尚未结束,还没有人获胜。

第 2 行包含一个整数,表示 T 时刻先手玩家的英雄的生命值。

第 3 行包含若干个整数,第一个整数 p 表示 T 时刻先手玩家在战场上存活的随从个数,之后 p 个整数,分别表示这些随从在 T 时刻的生命值(按照从左往右的顺序)。

第 4 行和第 5 行与第 2 行和第 3 行类似,只是将玩家从先手玩家换为后手玩家。

样例输入

8

summon 1 3 6

summon 2 4 2

end

summon 1 4 5

summon 1 2 1

attack 1 2

end

attack 1 1

样例输出

0

30

1 2

30

1 2

样例说明

按照样例输入从第 2 行开始逐行的解释如下:

1. 先手玩家在位置 1 召唤一个生命值为 6、攻击力为 3 的随从 A,是本方战场上唯一的随从。

2. 先手玩家在位置 2 召唤一个生命值为 2、攻击力为 4 的随从 B,出现在随从 A 的右边。

3. 先手玩家回合结束。

4. 后手玩家在位置 1 召唤一个生命值为 5、攻击力为 4 的随从 C,是本方战场上唯一的随从。

5. 后手玩家在位置 1 召唤一个生命值为 1、攻击力为 2 的随从 D,出现在随从 C 的左边。

6. 随从 D 攻击随从 B,双方均死亡。

7. 后手玩家回合结束。

8. 随从 A 攻击随从 C,双方的生命值都降低至 2。

评测用例规模与约定

  * 操作的个数0 ≤ n ≤ 1000。
  * 随从的初始生命值为 1 到 100 的整数,攻击力为 0 到 100 的整数。
  * 保证所有操作均合法,包括但不限于:
  1) 召唤随从的位置一定是合法的,即如果当前本方战场上有 m 个随从,则召唤随从的位置一定在 1 到 m + 1 之间,其中 1 表示战场最左边的位置,m + 1 表示战场最右边的位置。
  2) 当本方战场有 7 个随从时,不会再召唤新的随从。
  3) 发起攻击和被攻击的角色一定存在,发起攻击的角色攻击力大于 0。
  4) 一方英雄如果死亡,就不再会有后续操作。
  * 数据约定:
  前 20% 的评测用例召唤随从的位置都是战场的最右边。
  前 40% 的评测用例没有 attack 操作。
  前 60% 的评测用例不会出现随从死亡的情况。

分析:

问题分析:本体要求用户模拟《炉石传说》游戏,双方对弈,回合制游戏。初始双方均只有英雄角色一个和最多七个随从角色卡槽。英雄初始生命值30,攻击力0;每轮双方可在召唤操作中召唤自己的随从(如果卡槽有空位),召唤随从时可指定放入卡槽位置,攻击力和生命值。同时,每轮双方的攻击操作中,可以指定己方攻击者和对方被攻击者(以卡槽位为标志,英雄卡槽位记为0),攻击双方对应角色都会受到来自对方角色的攻击值伤害,攻击操作结束后,生命值小于等于0的角色将阵亡。每轮各方均可有角色召唤,攻击零次或多次操作,并以“end”作为每方的结束标志。若某方的英雄角色阵亡,则对方获胜。输入一个整数n表示总的操作数,其后n行表示进行的n个操作;输出获胜方(1先手方,0尚未决出胜负,-1后手方)以及先后手方此时个角色剩余数量及分别的生命值。

设计求解思路:可以先创建一个Role类,其中包含生命值和攻击力两个整数属性,提供构造方法用于实例化对象时初始化两个成员变量。再创建两个ArrayList集合listA和listB,类型为Hero类型,并分别添加new Role(30,0)表示各自的英雄。在构建一个布尔类型变量turnA赋初值true,表示当前轮次执行者是先手玩家A。对每轮操作,读取操作指令cmd。若为召唤操作,则读入召唤的位置position,health和attack值,通过在ArrayList集合中position索性位置插入new Role(health, attack)元素来实现。若为攻击操作,则读取attacker和defender,通过ArrayList中的get方法获取对应元素进行生命值操作。若为end操作,则反转turnA(turnA=!turnA)。最后按题干要求输出对应结果。

代码实现:

import java.util.ArrayList;
import java.util.Scanner;

/** @author: Linux_Mumu */
public class Main {
    public static class Role {
        int attack, health;

        Role(int health, int attack) {
            this.health = health;
            this.attack = attack;
        }
    }

    public static void main(String[] args) throws Exception {
        String cmd;
        int n, attacker, defender, position, attack, health;
        boolean turnA = true;
        ArrayList<Role> listA = new ArrayList<>();
        ArrayList<Role> listB = new ArrayList<>();
        listA.add(new Role(30, 0));
        listB.add(new Role(30, 0));
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        for (int i = 0; i < n; i++) {
            cmd = sc.next();
            if ("summon".equals(cmd)) {
                position = sc.nextInt();
                attack = sc.nextInt();
                health = sc.nextInt();
                if (turnA) {
                    listA.add(position, new Role(health, attack));
                } else {
                    listB.add(position, new Role(health, attack));
                }
            } else if ("attack".equals(cmd)) {
                attacker = sc.nextInt();
                defender = sc.nextInt();
                if (turnA) {
                    listA.get(attacker).health -= listB.get(defender).attack;
                    listB.get(defender).health -= listA.get(attacker).attack;
                    if (listA.get(attacker).health <= 0) {
                        listA.remove(attacker);
                    }
                    if (defender != 0 && listB.get(defender).health <= 0) {
                        listB.remove(defender);
                    }
                } else {
                    listB.get(attacker).health -= listA.get(defender).attack;
                    listA.get(defender).health -= listB.get(attacker).attack;
                    if (listB.get(attacker).health <= 0) {
                        listB.remove(attacker);
                    }
                    if (defender != 0 && listA.get(defender).health <= 0) {
                        listA.remove(defender);
                    }
                }
            } else {
                turnA = !turnA;
            }
        }
        if (listA.get(0).health > 0 && listB.get(0).health > 0) {
            System.out.println("0");
        } else {
            if (listA.get(0).health > 0) {
                System.out.println("1");
            } else {
                System.out.println("-1");
            }
        }
        System.out.println(listA.get(0).health);
        System.out.print(listA.size() - 1);
        for (int i = 1; i < listA.size(); i++) {
            System.out.print(" ");
            System.out.print(listA.get(i).health);
        }
        System.out.println();
        System.out.println(listB.get(0).health);
        System.out.print(listB.size() - 1);
        for (int i = 1; i < listB.size(); i++) {
            System.out.print(" ");
            System.out.print(listB.get(i).health);
        }
        System.out.println();
        sc.close();
    }
}

P.S. 第一次写技术博客,不足及谬误之处还请大家批评指正,谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值