201521123122 《java程序设计》第七周学习总结
201521123122 《java程序设计》第七周实验总结
1. 本周学习总结
以你喜欢的方式(思维导图或其他)归纳总结集合相关内容。
2. 书面作业
ArrayList代码分析
1.1 解释ArrayList的contains源代码
先贴源代码:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
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;
}
1.elementData 是"Object[]类型的数组",它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长
2.“null”的含义:在JVM规范中是这么说的:
,也就是说null是指一个不确定的对象,在java中,null对象不能使用equal函数,所以要区分是否为null。
3.代码的意思就是验证ArrayList中是否包含所查询的对象,如果存在,则返回其对象所在的位置,不存在的话,返回-1;
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;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
1.rangeCheck函数的作用在于,如果想要删除的位置在size外面,则抛出一个IndexOutOfBoundsException的异常。
2.主要是把index这个位置的元素取出来,然后后面的元素位置依次向前进一位,然后最后一个元素设置为null;总体size减1;
1.3 结合1.1与1.2,回答ArrayList存储数据时需要考虑元素的类型吗?
应考虑是否为基本数据类型,除基本数据类型外的其他类型都可以存储。
1.4 分析add源代码,回答当内部数组容量不够时,怎么办?
同理,先贴代码:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // ensureCapacityInternal用来调整容量
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //如果超出容量,则调用grow方法增加容量
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //增加原来容量的一半(右移一位就是/2),也就是说新List的容量是旧的1.5倍
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); //把旧数组拷贝至新数组,这里说明了并不是增加原来数组的大小,而是引用了一个大小为原来数组1.5倍的新数组。
}
从代码以及写的注释中可以看出,如果超出容量,会调用一个grow方法,引用一个新数组,其大小为原来数组的1.5倍,然后把旧数组拷贝至新数组。
1.5 分析private void rangeCheck(int index)源代码,为什么该方法应该声明为private而不声明为public?
rangeCheck代码在上面,我就不复制了。其根本原因还在于java的封装性,因为用户在用这个函数的时候,完全不用去考虑其内部结构,只需知道能否使用就可以了。
HashSet原理
2.1 将元素加入HashSet(散列集)中,其存储位置如何确定?需要调用那些方法?
1.set中不能存储相同的元素,所以要用equal()方法来保证元素的唯一性
2.从HashSet中访问元素时,HashSet先计算该元素的hashCode值,然后到该hashCode对应的位置取出该元素。所以要调用HashCode算法。
2.2 选做:尝试分析HashSet源代码后,重新解释2.1
以下转自博客http://www.cnblogs.com/ITtangtang/p/3948538.html
对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成,
源代码如下:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
4{
static final long serialVersionUID = -5024744406713321676L;
// 底层使用HashMap来保存HashSet中所有元素。
private transient HashMap<E,Object> map;
// 定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。
private static final Object PRESENT = new Object();
/**
* 默认的无参构造器,构造一个空的HashSet。
*
* 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
*/
public HashSet() {
map = new HashMap<E,Object>();
}
/**
* 构造一个包含指定collection中的元素的新set。
*
* 实际底层使用默认的加载因子0.75和足以包含指定
* collection中所有元素的初始容量来创建一个HashMap。
* @param c 其中的元素将存放在此set中的collection。
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 以指定的initialCapacity和loadFactor构造一个空的HashSet。
*
* 实际底层以相应的参数构造一个空的HashMap。
* @param initialCapacity 初始容量。
* @param loadFactor 加载因子。
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
/**
* 以指定的initialCapacity构造一个空的HashSet。
*
* 实际底层以相应的参数及加载因子loadFactor为0.75构造一个空的HashMap。
* @param initialCapacity 初始容量。
*/
public HashSet(int initialCapacity) {
map = new HashMap<E,Object>(initialCapacity);
}
/**
* 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。
* 此构造函数为包访问权限,不对外公开,实际只是是对LinkedHashSet的支持。
*
* 实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。
* @param initialCapacity 初始容量。
* @param loadFactor 加载因子。
* @param dummy 标记。
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}
/**
* 返回对此set中元素进行迭代的迭代器。返回元素的顺序并不是特定的。
*
* 底层实际调用底层HashMap的keySet来返回所有的key。
* 可见HashSet中的元素,只是存放在了底层HashMap的key上,
* value使用一个static final的Object对象标识。
* @return 对此set中元素进行迭代的Iterator。
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
/**
* 返回此set中的元素的数量(set的容量)。
*
* 底层实际调用HashMap的size()方法返回Entry的数量,就得到该Set中元素的个数。
* @return 此set中的元素的数量(set的容量)。
*/
public int size() {
return map.size();
}
/**
* 如果此set不包含任何元素,则返回true。
*
* 底层实际调用HashMap的isEmpty()判断该HashSet是否为空。
* @return 如果此set不包含任何元素,则返回true。
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* 如果此set包含指定元素,则返回true。
* 更确切地讲,当且仅当此set包含一个满足(o==null ? e==null : o.equals(e))
* 的e元素时,返回true。
*
* 底层实际调用HashMap的containsKey判断是否包含指定key。
* @param o 在此set中的存在已得到测试的元素。
* @return 如果此set包含指定元素,则返回true。
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
/**
* 如果此set中尚未包含指定元素,则添加指定元素。
* 更确切地讲,如果此 set 没有包含满足(e==null ? e2==null : e.equals(e2))
* 的元素e2,则向此set 添加指定的元素e。
* 如果此set已包含该元素,则该调用不更改set并返回false。
*
* 底层实际将将该元素作为key放入HashMap。
* 由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key
* 与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true),
* 新添加的Entry的value会将覆盖原来Entry的value,但key不会有任何改变,
* 因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中,
* 原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。
* @param e 将添加到此set中的元素。
* @return 如果此set尚未包含指定元素,则返回true。
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* 如果指定元素存在于此set中,则将其移除。
* 更确切地讲,如果此set包含一个满足(o==null ? e==null : o.equals(e))的元素e,
* 则将其移除。如果此set已包含该元素,则返回true
* (或者:如果此set因调用而发生更改,则返回true)。(一旦调用返回,则此set不再包含该元素)。
*
* 底层实际调用HashMap的remove方法删除指定Entry。
* @param o 如果存在于此set中则需要将其移除的对象。
* @return 如果set包含指定元素,则返回true。
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
/**
* 从此set中移除所有元素。此调用返回后,该set将为空。
*
* 底层实际调用HashMap的clear方法清空Entry中所有元素。
*/
public void clear() {
map.clear();
}
/**
* 返回此HashSet实例的浅表副本:并没有复制这些元素本身。
*
* 底层实际调用HashMap的clone()方法,获取HashMap的浅表副本,并设置到 HashSet中。
*/
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
}
ArrayListIntegerStack
题集jmu-Java-05-集合之5-1 ArrayListIntegerStack
3.1 比较自己写的ArrayListIntegerStack与自己在题集jmu-Java-04-面向对象2-进阶-多态、接口与内部类中的题目5-3自定义接口ArrayIntegerStack,有什么不同?(不要出现大段代码)
1.ArrayListIntegerStack是用ArrayList对象存储的,ArrayIntegerStack是用Integer存储的。
2.ArrayIntegerStack在出栈的时候需要移动top指针,ArrayListIntegerStack不需要top指针,调用ArrayList自有的remove()方法就可以。
3.2 简单描述接口的好处.
简单来说,就是可以通过一个接口来控制不同的类。
Stack and Queue
4.1 编写函数判断一个给定字符串是否是回文,一定要使用栈,但不能使用java的Stack类(具体原因自己搜索)。请粘贴你的代码,类名为Main你的学号。
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
@SuppressWarnings("unused")
public class Main201521123122{
public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner sc=new Scanner(System.in);
System.out.println("请输入字符串");
ArrayListStringStack Stack=new ArrayListStringStack();
String n= sc.next();
for(int i=0;i<n.length();i++)
{
Stack.push(String.valueOf(n.charAt(i)));
}
for(int j=0;j<n.length();j++){
if(String.valueOf(n.charAt(j)).equals(Stack.pop())){
System.out.println("是回文");
break;
}
else {
System.out.println("不是回文");
break;
}
}
}
public class ArrayListStringStack implements StringStack {
private ArrayList<String> list;
public ArrayListStringStack() {
list=new ArrayList<String>();
}
public String push(String item) {
if(item==null)
return null;
list.add(item);
return item;
}
@Override
public String pop() {
if(list.isEmpty())
return null;
return list.remove(list.size()-1);
}
}
public interface StringStack {
public String push(String item);
public String pop();
}
4.2 题集jmu-Java-05-集合之5-6 银行业务队列简单模拟。(不要出现大段代码)
只需设置两个队列,然后奇数队列出两个,偶数队列出一个,知道其中一个为空,然后输出剩下的队列
代码如下:
int n=in.nextInt();
for( int i = 0; i < n; i++ ) {
int x=in.nextInt();
if( x % 2==0 ) qB.add( x );
else qA.add( x );
}
偶数放在B窗口,奇数放在A窗口
while(!qA.isEmpty()||!qB.isEmpty()){
Integer a1=qA.poll();
if(a1!=null){
if(qA.isEmpty()&&qB.isEmpty())
System.out.print(a1);
else System.out.print(a1+" ");
}
Integer a2=qA.poll();
if(a2!=null){
if(qA.isEmpty()&&qB.isEmpty())
System.out.print(a2);
else System.out.print(a2+" ");
}
Integer b=qB.poll();
if(b!=null){
if(qA.isEmpty()&&qB.isEmpty())
System.out.print(b);
else System.out.print(b+" ");
}
}
输出两个A,在输出一个B
统计文字中的单词数量并按单词的字母顺序排序后输出
题集jmu-Java-05-集合之5-2 统计文字中的单词数量并按单词的字母顺序排序后输出 (不要出现大段代码)
代码如下:
public static void main(String[] args) {
// TODO Auto-generated method stub
Set<String>set=new TreeSet();
Scanner in=new Scanner(System.in);
while(in.hasNext()){
String str=in.next();
if(str.equals("!!!!!"))break;
set.add(str);
}
System.out.println(set.size());
if(set.size()<10)
for (String string : set) {
System.out.println(string);
}
else{
for (int i = 0; i < 10; i++) {
System.out.println(set.toArray()[i]);
}
}
}
5.1 实验总结
用while(in.hasNext()),来拆分文章中的单词,而使用Treeset会自动将单词排好序,直接输出就可以。
3. 码云上代码提交记录及PTA实验总结
题目集:jmu-Java-05-集合
3.1. 码云代码提交记录
在码云的项目中,依次选择“统计-Commits历史-设置时间段”, 然后搜索并截图