1. 本周学习总结
- List 必须按照插入的顺序保存元素
- Set不能有重复元素
- ArrayList ,它长于随机访问元素,但是在List的中阅插入和移除元素时较慢。
- LinkedList,它进行插入和删除操作代价较低, LinkedList在随机访问方面相对比较慢。LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。
- iterator()方放返回的是实现 Iterator的匿名内部类的实例
- HashMap、HashSet提供快速查询、访问;TreeMap、TreeSet有持续排序特性。
- 新程序中不应该使用过时的Vector、Hashtable、stack
2. 书面作业
1. ArrayList代码分析
1.1 解释ArrayList的contains源代码
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
答: 通过代码与英文注释可以看出,contains
方法调用了indexOf
方法。其内部是用数组实现的,如果在数组中找到传入的元素,则返回其下标数字,如果没有找到则返回-1。根据indexOf
方法的返回值,-1代表不包含此元素,若>=0则包含。为防止空指针错误,indexOf
方法分为null
与非null
两种情况。
1.2 解释E remove(int index)源代码
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
答: 此方法作用在于移除某个元素,并将其后面元素左移一位。首先通过rangeCheck(index);
方法查询是否传入下标超过边界。numMoved
用来记录元素移动个数。System.arraycopy(elementData, index+1, elementData, index,numMoved);
将index+1开始的数组复制到index位。随后将size--,再将最后一位设置为null,返回删除元素。
1.3 结合1.1与1.2,回答ArrayList存储数据时需要考虑元素的具体类型吗?
答: 不需要考虑。
理论上来说,ArrayList不能储存基本数据类型,但存至ArrayList的基本数据类型会进行自动包装。
1.4 分析add源代码,回答当内部数组容量不够时,怎么办?
答:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
通过ensureCapacityInternal(size + 1);
方法确保容量足够。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
此方法先验证elementData是否为空数组,如果为空,则在10与传入的长度参数中选择最大值作为数组长度。
随后,数组长度如果不够,则调用grow(minCapacity);
方法。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
通过grow(minCapacity);
方法。容量增大原来的一半。最后判断数组容量,如果小于0则报错,如果大于最大容量则将容量设为最大容量MAX_ARRAY_SIZE
1.5 分析private void rangeCheck(int index)源代码,为什么该方法应该声明为private而不声明为public?
答: 因为面向对象编程需要将一些细节隐藏起来,这样既简化了程序员的编写流程,也增加了安全性。
2. HashSet原理
2.1 将元素加入HashSet(散列集)中,其存储位置如何确定?需要调用那些方法?
答: 系统通过 Hash 算法决定集合元素的存储位置。调用map.put(e, PRESENT)
方法。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
计算Hash值确定存储位置,如果此位置没有元素,则将其存储在这一位置,如果有元素则判断HASH值是否相同,如果相同则将其赋给此位置,如果不同则继续循环链表加在后面。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
如果Key值为null,返回0;如果Key值不为空,返回原hash值和原hash值无符号右移16位的值按位异或的结果,在从网上查阅资料后,了解到这是“扰动函数”,作用在于减少碰撞次数,增加效率。
2.2 将元素加入HashSet中的时间复杂度是多少?是O(n)吗?(n为HashSet中已有元素个数)
答: 时间复杂度为O(1)
2.3 选做:尝试分析HashSet源代码后,重新解释2.1
2.1分析了一部分,但putval
方法内部不是很理解。
3. ArrayListIntegerStack
3.1 比较自己写的ArrayListIntegerStack与自己在题集jmu-Java-04-面向对象2-进阶-多态、接口与内部类中的题目自定义接口ArrayIntegerStack,有什么不同?(不要出现大段代码)
实现方式上:private Integer[] stack;
与List<Integer> stack = new ArrayList<Integer>();
两种实现方式各有特点。
ArrayList不存在栈满的情况,因为它会自动扩充容量。
当要删除元素时ArrayList直接调用remove,而数组则是调整top值作为下标。
3.2 结合该题简单描述接口的好处,需以3.1为例详细说明,不可泛泛而谈。
答: 我们在编写此题时,因为题目已经给出了实现方式,所以我们不需要考虑实现方式的优缺点。
但当我们要用LinkedList是现实,如果没有接口,我们就只能重写所有代码。存在接口,实现方式可以被简便的替换,而不影响其他内部方法与实现。
同时我们也可以明确这个接口的作用,使得代码更加规范化、流程化。我们一眼就可以明晰public Integer push(Integer item)
是入栈,public Integer pop()
是出栈。
4. Stack and Queue
4.1 编写函数判断一个给定字符串是否是回文,一定要使用栈(请利用Java集合中已有的类),但不能使用java的Stack类(具体原因自己搜索)与数组。请粘贴你的代码,类名为Main你的学号。
package week7;
import java.util.LinkedList;
import java.util.Scanner;
class Stack<Object> {
private LinkedList<Object> stack = new LinkedList<Object>();
public void push(Object obj) {
stack.addFirst(obj);
}
public Object peek() {
return stack.getFirst();
}
public Object pop() {
return stack.removeFirst();
}
public boolean empty() {
return stack.isEmpty();
}
public String toString() {
return stack.toString();
}
}
public class Main201621123086 {
public static void main(String[] args) {
Stack<Character> stack = new Stack<Character>();
Scanner sc = new Scanner(System.in);
char[] str = new char[20];
while (sc.hasNextLine()){
String string = sc.nextLine();
boolean flag = true;
for (int i = 0; i < string.length(); i++) {
stack.push(string.charAt(i));
}
for (int i = 0; i < string.length(); i++) {
if (stack.pop() != string.charAt(i)) {
System.out.println("不是回文");
flag = false;
break;
}
}
if (flag)
System.out.println("是回文");}
}
}
4.2 题集jmu-Java-05-集合之银行业务队列简单模拟(只粘贴关键代码)。请务必使用Queue接口,并说明你使用了Queue接口的哪一个实现类?
LinkedList实现类
5. 统计文字中的单词数量并按单词的字母顺序排序后输出
5.1 实验总结
通过set来过滤重复元素,通过TreeSet排序元素
7. 选做 面向对象设计大作业-改进
7.1 使用集合类改进大作业或者简述你的面向对象设计大作业中,哪里使用到了集合类。
主要在商品列表与购物车列表运用了ArrayList集合类。
同时,也可以在购物车列表上运用Hashset来避免重复加入。
7.2 进一步完善图形界面(仅需出现改进的后的图形界面截图)
主要是在购物车上完成了总价的输出,以及解决购物车列表的刷新
3.码云及PTA
3.1. 码云代码提交记录
3.2 截图PTA题集完成情况图
3.3 统计本周完成的代码量
周次 | 总代码量 | 新增代码量 | 总文件数 | 新增文件数 |
---|---|---|---|---|
1 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0 | 0 |
3 | 619 | 619 | 15 | 15 |
4 | 619 | 0 | 15 | 0 |
5 | 973 | 354 | 25 | 10 |
6 | 1394 | 421 | 37 | 12 |
7 | 2001 | 607 | 43 | 6 |
8 | 4046 | 2045 | 54 | 11 |
9 | 4486 | 440 | 64 | 10 |