作业08-集合


1.本周学习总结

以你喜欢的方式(思维导图或其他)归纳总结集合相关内容。

2.书面作业

1. ArrayList代码分析

1.1 解释ArrayList的contains源代码

源代码如下:

  1. public boolean contains(Object o) {
  2. return indexOf(o) >= 0;
  3. }
  1. public int indexOf(Object o) { //遍历元素看是否存在元素“o”
  2. if (o == null) {
  3. for (int i = 0; i < size; i++)
  4. if (elementData[i]==null)
  5. return i;
  6. } else {
  7. for (int i = 0; i < size; i++)
  8. if (o.equals(elementData[i]))
  9. return i;
  10. }
  11. return -1;
  12. }

答:由上述源代码可以看出ArrayListcontain方法是通过调用indexOf()方法判断元素在数组中的位置来确定是否存在该元素的。在indexOf()方法中,为了防止null去调用equals()方法而产生空指针异常,方法将元素分为了非null元素和null元素分别进行处理,在对象不为null时,最终调用的是Object类的equals()方法来确定其是否存在。

1.2 解释E remove(int index)源代码

答:源代码如下:

  1. public E remove(int index) {
  2. rangeCheck(index);
  3. modCount++; //修改次数+1
  4. E oldValue = elementData(index);
  5. int numMoved = size - index - 1;
  6. if (numMoved > 0)
  7. System.arraycopy(elementData, index+1, elementData, index,
  8. numMoved); //System类中的一个本地方法复制
  9. elementData[--size] = null; // 删除数组最后一个元素并修改大小
  10. return oldValue;
  11. }
  1. private void rangeCheck(int index) {
  2. if (index >= size)
  3. throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  4. }

由源代码可以看出,remove方法首先调用了rangeCheck方法来判断数组是否越界,然后再删除指定位置的元素并进行数组长度的修改。

1.3 结合1.1与1.2,回答ArrayList存储数据时需要考虑元素的具体类型吗?

答:不需要,由上面两题可以看出,数组中存放的元素的类型为Object类型,它是所有类的父类,在传入数据时都是向上转型,因此不需要考虑元素的具体类型。

1.4 分析add源代码,回答当内部数组容量不够时,怎么办?

答:源代码如下:

  1. //将数据添加尾部的add方法
  2. public boolean add(E e) {
  3. ensureCapacityInternal(size + 1); // Increments modCount!!
  4. elementData[size++] = e;
  5. return true;
  6. }
  1. private void ensureCapacityInternal(int minCapacity) {
  2. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  3. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
  4. }
  5. ensureExplicitCapacity(minCapacity);
  6. }
  7. private void ensureExplicitCapacity(int minCapacity) {
  8. modCount++;
  9. // overflow-conscious code
  10. if (minCapacity - elementData.length > 0)
  11. grow(minCapacity);
  12. }

首先调用了ensureCapacityInternal方法判断是否要进行数组的扩容,说到容量的话,ArrayList继承于List接口,每个ArrayList实例都有一个容量,在创建对象时没有指定长度的话,初始容量会被设置为10,然后随着元素的增多而增长。当判断为需要扩容时则调用ensureExplicitCapacity实现数组的扩容。最后再修改对象数组的长度。

  1. //在指定位置添加元素的add方法
  2. public void add(int index, E element) {
  3. rangeCheckForAdd(index);
  4. ensureCapacityInternal(size + 1); // Increments modCount!!
  5. System.arraycopy(elementData, index, elementData, index + 1,
  6. size - index);
  7. elementData[index] = element;
  8. size++;
  9. }
  1. private void rangeCheckForAdd(int index) {
  2. if (index > size || index < 0)
  3. throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  4. }

这个方法其实和上一个差不多,只是多了要将插入元素位置后的剩余元素的下标进行修改的操作。

1.5 分析private void rangeCheck(int index)源代码,为什么该方法应该声明为private而不声明为public?

答:修饰为private体现了其封装性,rangeCheck方法是在remove元素时用于判断是否出现数组越界的情况,这个只需要在本类中使用,而用户只需要知道remove方法的使用,至于其内部的具体实现他们就没必要知道了。

2. HashSet原理

2.1 将元素加入HashSet(散列集)中,其存储位置如何确定?需要调用那些方法?

答:在将元素加入HashSet的时候,首先先调用hashCode方法来计算元素的哈希码的值,然后再由哈希码的值计算出元素的存储位置,如果指定的位置没有值的话,则将元素添加到哈希表中,但是如果算出的位置上已经有了一个值的话,就要调用equals方法比较该位置上的值和待加入的值,如果两个值相同则不将元素添加进哈希表,因为HashSet不会存储重复的元素,如果两个值不同就使用解决冲突的方法将元素添加到相应的哈希表的位置中。

2.2 将元素加入HashSet中的时间复杂度是多少?是O(n)吗?(n为HashSet中已有元素个数)

答:HashSet是使用哈希表实现的,其中的元素是无序的,在添加元素的时候不需要遍历,是直接通过哈希算法来计算位置来直接进行元素的添加,所以时间复杂度为O(1)。

2.3 选做:尝试分析HashSet源代码后,重新解释2.1

答:查看了HashSet的源代码后发现它居然是基于HashMap实现的,它的底层还使用了HashMap来存储元素。

  1. public boolean add(E e) {
  2. return map.put(e, PRESENT)==null;
  3. }
  4. public boolean remove(Object o) {
  5. return map.remove(o)==PRESENT;
  6. }
  7. public void clear() {
  8. map.clear();
  9. }

然后通过HashSetadd源码追溯到HashMapput方法上

  1. public V put(K key, V value) {
  2. return putVal(hash(key), key, value, false, true);
  3. }
  4. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
  5. boolean evict) {
  6. Node<K,V>[] tab; Node<K,V> p; int n, i;
  7. if ((tab = table) == null || (n = tab.length) == 0)
  8. n = (tab = resize()).length;
  9. if ((p = tab[i = (n - 1) & hash]) == null)
  10. tab[i] = newNode(hash, key, value, null);
  11. else {
  12. Node<K,V> e; K k;
  13. if (p.hash == hash &&
  14. ((k = p.key) == key || (key != null && key.equals(k))))
  15. e = p;
  16. else if (p instanceof TreeNode)
  17. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  18. else {
  19. for (int binCount = 0; ; ++binCount) {
  20. if ((e = p.next) == null) {
  21. p.next = newNode(hash, key, value, null);
  22. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  23. treeifyBin(tab, hash);
  24. break;
  25. }
  26. if (e.hash == hash &&
  27. ((k = e.key) == key || (key != null && key.equals(k))))
  28. break;
  29. p = e;
  30. }
  31. }
  32. if (e != null) { // existing mapping for key
  33. V oldValue = e.value;
  34. if (!onlyIfAbsent || oldValue == null)
  35. e.value = value;
  36. afterNodeAccess(e);
  37. return oldValue;
  38. }
  39. }
  40. ++modCount;
  41. if (++size > threshold)
  42. resize();
  43. afterNodeInsertion(evict);
  44. return null;
  45. }

由上面的源代码可以看出,在put方法中实际上是返回了putVal方法,其基本的过程如下。

put操作的基本流程:

(1)通过hash值得到所在bucket的下标,如果为null,表示没有发生碰撞,则直接put

(2)如果发生了碰撞,则解决发生碰撞的实现方式:链表还是树。

(3)如果能够找到该key的结点,则执行更新操作,无需对modCount增1。

(4)如果没有找到该key的结点,则执行插入操作,需要对modCount增1。

(5)在执行插入操作时,如果bucket中bin的数量超过TREEIFY_THRESHOLD,则要树化。

(6)在执行插入操作之后,如果size超过了threshold,这要扩容。

3. ArrayListIntegerStack

题集jmu-Java-05-集合之ArrayListIntegerStack

3.1 比较自己写的ArrayListIntegerStack与自己在题集jmu-Java-04-面向对象2-进阶-多态、接口与内部类中的题目自定义接口ArrayIntegerStack,有什么不同?(不要出现大段代码)

  1. //本次题集中的栈的定义
  2. List<Integer> stack = new ArrayList<Integer>();
  3. //上次题目中的栈的定义
  4. Integer[] Stack;
  5. private static int top=0;
  6. public ArrayIntegerStack(int n){
  7. Stack = new Integer[n];
  8. }

通过比较可以看出,最明显的不同当然是存储形式的不同,上次的题目中使用的是数组的方法来存储栈,而这次采用的则是动态数组来进行存储。也正是由于这点的不同,导致了后面对栈的操作的方法的一些不同。

对于入栈来说,由于动态数组能够自动扩容,因此与普通数组的入栈操作相比少了判断栈满的情况。

而其他的方法,像出栈或是取栈顶元素,主要的不同都是由栈顶指针引起的,在动态数组中可以通过其size方法来获得栈顶的位置,而在普通数组中则要通过自定义一个栈顶指针top,然后根据数组的出入栈进行指针的移动来实现其功能。

3.2 结合该题简单描述接口的好处,需以3.1为例详细说明,不可泛泛而谈。

答:在这题中我们用到了接口IntegerStack,这个接口和我们上次作业中的接口一样,因此我们只要重新写一个类来实现这个接口,然后根据要求实现其中的方法即可,如果我们没有定义接口,那我们就只能重新写一个栈的类然后编写方法实现,这和使用接口比就增加了我们的代码量,由此可见,使用接口来实现便于程序的维护和拓展,也使我们代码更具有了灵活性,我们可以根据不同的工作环境来使用不同的方式来实现接口里的方法。

4. Stack and Queue

4.1 编写函数判断一个给定字符串是否是回文,一定要使用栈(请利用Java集合中已有的类),但不能使用java的Stack类(具体原因自己搜索)与数组。请粘贴你的代码,类名为Main你的学号


  1. import java.util.*;
  2. class Stack2 {
  3. Deque<Character> stack = new LinkedList<Character>();
  4. public void push(Character item) {
  5. stack.addLast(item);
  6. }
  7. public void pop() {
  8. stack.removeLast();
  9. }
  10. public Character peek() {
  11. return stack.getLast();
  12. }
  13. public boolean empty() {
  14. return stack.isEmpty();
  15. }
  16. public int size() {
  17. return stack.size();
  18. }
  19. }
  1. public class Main201621123031 {
  2. public static void main(String[] args) {
  3. Scanner sc = new Scanner(System.in);
  4. Stack2 stack = new Stack2();
  5. boolean flag = true;
  6. String str = sc.next();
  7. char[] ch = str.toCharArray();
  8. for(char e:ch){
  9. stack.push(e);
  10. }
  11. for(int i=0;i<stack.size();i++){
  12. if(ch[i]!=stack.peek()){
  13. flag=false;
  14. break;
  15. }
  16. stack.pop();
  17. }
  18. System.out.println(flag);
  19. sc.close();
  20. }
  21. }

运行结果:



4.2 题集jmu-Java-05-集合之银行业务队列简单模拟(只粘贴关键代码)。请务必使用Queue接口,并说明你使用了Queue接口的哪一个实现类?

  1. //两个队列的定义
  2. Queue<Integer> listA = new LinkedList<Integer>();
  3. Queue<Integer> listB = new LinkedList<Integer>();
  1. //入队操作
  2. for (int i = 0; i < m; i++) {
  3. int a = sc.nextInt();
  4. if (a % 2 == 1)
  5. listA.add(a);
  6. else
  7. listB.add(a);
  8. }
  1. //业务处理
  2. while (!listA.isEmpty() && !listB.isEmpty()) {
  3. if (!listA.isEmpty()) {
  4. System.out.print(listA.poll() + " ");
  5. if (!listA.isEmpty()) {
  6. System.out.print(listA.poll() + " ");
  7. }
  8. }
  9. Integer a = listB.poll();
  10. if (listA.isEmpty() && listB.isEmpty())
  11. System.out.print(a);
  12. else
  13. System.out.print(a + " ");
  14. }
  15. while (!listA.isEmpty()) {
  16. for (int i = 0; i < listA.size() - 1; i++) {
  17. System.out.print(listA.poll() + " ");
  18. }
  19. System.out.print(listA.poll());
  20. }
  21. while (!listB.isEmpty()) {
  22. for (int i = 0; i < listB.size() - 1; i++) {
  23. System.out.print(listB.poll() + " ");
  24. }
  25. System.out.print(listB.poll());
  26. }

本题使用了LinkedList实现类,LinkedList是采用链表的形式实现的,适合队列的出队入队操作,而以数组形式实现的实现类更加适合于随机访问。

5. 统计文字中的单词数量并按单词的字母顺序排序后输出

题集jmu-Java-05-集合之5-2 统计文字中的单词数量并按单词的字母顺序排序后输出 (作业中不要出现大段代码)

5.1 实验总结

答:这题用到了TreeSet来存储数据,因为其具有排序功能且能够不添加重复的元素,然后刚开始先使用了split来分割字符串来实现数据的添加,但是这个要考虑空行或是多个空格的因素,比较不方便,所以后面直接用了next的输入方法进行输入,接着在输出前十个元素的时候,发现Set集合不像List集合,可以通过get方法获取指定位置的元素,因此只能采用foreach语句,然后添加一个计数器来实现输出指定个数的数字。

6. 选做:统计文字中的单词数量并按出现次数排序

题集jmu-Java-05-集合之5-3 统计文字中的单词数量并按出现次数排序(不要出现大段代码)

6.1 伪代码

  1. 创建HashMap对象map
  2. 循环读入文章中的单词
  3. IF 读入单词为”!!!!!”
  4. 退出循环
  5. ELSE
  6. IF map中没有相应的key
  7. 添加单词到map中并设置value1
  8. ELSE
  9. key对应的value值加1
  10. 创建ArrayList对象list
  11. 实现Collections接口对map对象进行排序
  12. 输出map的长度
  13. 输出排序后list中前十个数据

6.2 实验总结

答:这题中用到了HashMap,key值为单词,value值为单词出现的次数,这题比较复杂的地方就是要使用一个能够存放Map数据ArrayList来实现Collections接口来实现相应的排序。

关键代码如下:

  1. List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
  2. Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
  3. @Override
  4. public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
  5. if (o1.getValue() == o2.getValue()) {
  6. return o1.getKey().compareTo(o2.getKey());
  7. }
  8. return o2.getValue() - o1.getValue();
  9. }
  10. });

其中Map.Entry为Map的一个内部接口,它表示Map中的一个实体(一个key-value对)。接口中有getKey(),getValue方法。使用它可以更方便我们去访问Map中的元素。

7. 选做 面向对象设计大作业-改进

7.1 使用集合类改进大作业或者简述你的面向对象设计大作业中,哪里使用到了集合类。

在系统中,存储商品信息和存储用户基本信息时都用到了ArrayList。

7.2 进一步完善图形界面(仅需出现改进的后的图形界面截图)

上周的大作业没及时交上,后面改进完一起补上了,因此就附上上一篇博客的链接

3.码云及PTA

题目集:jmu-Java-05-集合

3.1. 码云代码提交记录

  • 在码云的项目中,依次选择“统计-Commits历史-设置时间段”, 然后搜索并截图

3.2 截图PTA题集完成情况图

需要有两张图(1. 排名图。2.PTA提交列表图)





3.3 统计本周完成的代码量

需要将每周的代码统计情况融合到一张表中。

周次 总代码量 新增代码量 总文件数 新增文件数
2 607 607 15 15
3 1642 1035 33 18
5 2044 402 42 9
6 2874 830 57 15
7 3161 287 63 6
8 4299 1138 72 9
9 4831 532 81 9

201621123031 《Java程序设计》第8周学习总结的更多相关文章

  1. 20145213《Java程序设计》第九周学习总结

    20145213<Java程序设计>第九周学习总结 教材学习总结 "五一"假期过得太快,就像龙卷风.没有一点点防备,就与Java博客撞个满怀.在这个普天同庆的节日里,根 ...

  2. 20145213《Java程序设计》第二周学习总结

    20145213<Java程序设计>第二周学习总结 教材学习内容总结 本周娄老师给的任务是学习教材的第三章--基础语法.其实我觉得还蛮轻松的,因为在翻开厚重的书本,一股熟悉的气息扑面而来, ...

  3. 20145213《Java程序设计》第一周学习总结

    20145213<Java程序设计>第一周学习总结 教材学习内容总结 期待了一个寒假,终于见识到了神秘的娄老师和他的Java课.虽说算不上金风玉露一相逢,没有胜却人间无数也是情理之中,但娄 ...

  4. 21045308刘昊阳 《Java程序设计》第九周学习总结

    21045308刘昊阳 <Java程序设计>第九周学习总结 教材学习内容总结 第16章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 数据库本身是个独立运行的应用程序 撰 ...

  5. 20145330孙文馨 《Java程序设计》第一周学习总结

    20145330孙文馨 <Java程序设计>第一周学习总结 教材学习内容总结 刚开始拿到这么厚一本书说没有压力是不可能的,开始从头看觉得很陌生进入不了状态,就稍微会有一点焦虑的感觉.于是就 ...

  6. 20145337 《Java程序设计》第九周学习总结

    20145337 <Java程序设计>第九周学习总结 教材学习内容总结 数据库本身是个独立运行的应用程序 撰写应用程序是利用通信协议对数据库进行指令交换,以进行数据的增删查找 JDBC可以 ...

  7. 20145337 《Java程序设计》第二周学习总结

    20145337 <Java程序设计>第二周学习总结 教材学习内容总结 Java可分基本类型与类类型: 基本类型分整数(short.int.long).字节(byte).浮点数(float ...

  8. 20145218《Java程序设计》第一周学习总结

    20145218 <Java程序设计>第一周学习总结 教材学习内容总结 今天下午看了Java学习的视频,感觉很是新奇,之前觉得Java学起来是艰难枯燥的,但通过第一章的学习觉得如果自己可以 ...

  9. 《Java程序设计》第九周学习总结

    20145224 <Java程序设计>第九周学习总结 第十六章 整合数据库 JDBC入门 ·数据库本身是个独立运行的应用程序 ·撰写应用程序是利用通信协议对数据库进行指令交换,以进行数据的 ...

  10. 《Java程序设计》第二周学习总结

    20145224陈颢文<Java程序设计>第二周学习总结 教材学习内容总结 一.类型.变量与运算符 1.类型 整数: 可细分为为short整数(占2字节),int整数(占4字节),long ...

随机推荐

  1. springboot集成Actuator

    Actuator监控端点,主要用来监控与管理. 原生端点主要分为三大类:应用配置类.度量指标类.操作控制类. 应用配置类:获取应用程序中加载的配置.环境变量.自动化配置报告等与SpringBoot应用 ...

  2. 【Elasticsearch全文搜索引擎实战】之Kibana搭建

    1. Kibana介绍 Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索.查看交互存储在Elasticsearch索引中的数据.使用Kibana,可以通过各种图表进行高 ...

  3. ssm整合快速入门程序(三)之Data类型转换器

    今天就写写springmvc配置Data类型转换器 首先在创建一个转换器的包cn.my.ssm.controller.converter,创建一个CustomDateConverter类实现Conve ...

  4. Postman教程——创建第一个集合

    系列文章首发平台为果冻想个人博客.果冻想,是一个原创技术文章分享网站.在这里果冻会分享他的技术心得,技术得失,技术人生.我在果冻想等待你,也希望你能和我分享你的技术得与失,期待. 什么是集合 集合是P ...

  5. Mysql遇到 is marked as crashed and should be repaired 问题解决方法

    遇到 找到mysql的安装目录的bin/myisamchk工具,在命令行中输入: myisamchk -c -r /var/lib/mysql/ambari/alert_current.MYI 问题解 ...

  6. NancyFX 第三章 Web框架

    如果使用Nancy作为一个WEB框架而言,会有什么不同?实际上很多. 在使用Nancy框架为网页添加Rest节点和路由和之前的Rest框架中是相同的,这方面没有什么需要学习的了.Nancy采用一贯的处 ...

  7. ngrx/store effects 使用总结1:计数器

    本教程案例github:https://github.com/axel10/ngrx_demo-counter-and-list angular2+ 的学习成本应该是三大框架中最高的一个,教程及案例稀 ...

  8. Java集合中的LinkedHashMap类

    jdk1.8.0_144 本文阅读最好先了解HashMap底层,可前往<Java集合中的HashMap类>. LinkedHashMap由于它的插入有序特性,也是一种比较常用的Map集合. ...

  9. 精解Mat类(一):基本数据类型-固定大小的 矩阵类(Matx) 向量类(Vector)

    一.基础数据类型 1.(基础)固定大小矩阵类 matx 说明: ①    基础矩阵是我个人增加的描述,相对于Mat矩阵类(存储图像信息的大矩阵)而言. ②    固定大小矩阵类必须在编译期间就知晓其维 ...

  10. MySQL多数据源笔记5-ShardingJDBC实战

    Sharding-JDBC集分库分表.读写分离.分布式主键.柔性事务和数据治理与一身,提供一站式的解决分布式关系型数据库的解决方案. 从2.x版本开始,Sharding-JDBC正式将包名.Maven ...