1.容器(Collection)
之前学的数组就是一种容器,它也可以存储各种不同类型的数据,但是他必须规定长度;
以下是一张各类API接口图片:
- Collection定义了一种存储一组对象的方法; list属于一种有序的可重复的集合;
- set是一种无序且不可重复的,如果重复,后面的数据会把前面的数据给覆盖掉;
- Map接口定义存储键(key)值(value)映射队,(这个就是要自己设置一个键与其中的值一一对应,详细的后面再说);
- 迭代器:可以用来遍历容器;
- ArrayList的源码里面是通过数组的方式进行存储,查询的时候速度快,但是插入的时候速度慢(例如你插入一个元素在某个位置 在数组的条件下,数组从你插入的那个位置开始,后面所有的数组元素都需要移动位置,线程不安全),ArrayList允许添加为null的元素,所以null也会占用一份空间;
- LinkedList底层通过链表的方式实现的,链表查的时候慢,但是插入、删除等操作速度快(效率高,线程比较vector,没那么安全);
- Vector :线程安全但是效率低;
以下是模仿ArrayList的一个类:
一定要记住什么是构造函数:没有void修饰,没有返回值
1.ArrayList基础方法模拟
//以下注释是源码的程序,数组起始长度在ArrayList中默认是10;
//ensureCapacityInternal(size + 1); // 这个主要是判断size是否还在数组默认长度范围之内,意思就是输入的值有没有超出数组下标,并且进行自主扩容;
//elementData[size++] = e;//这是最后将值赋值给数组,并使得size+1;
//下列函數ensureCapacityInternal就是用來判斷的 ;minCapacity=size+1;
//private void ensureCapacityInternal(int minCapacity) {
//ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
//}
//private void ensureExplicitCapacity(int minCapacity) {
//modCount++;
//if (minCapacity - elementData.length > 0)
//grow(minCapacity);
//}
//grow函数
//private void grow(int minCapacity) {
//int oldCapacity = elementData.length;//定义一个oldCapacity接受数组原先的长度;
// oldCapacity + (oldCapacity >> 1)相当于oldCapacity+(oldCapacity/2)
//int newCapacity = oldCapacity + (oldCapacity >> 1);//再定义一个newCapacity接受新增加的长度;
//if (newCapacity - minCapacity < 0)//选比较小的数
//newCapacity = minCapacity;
// MAX_ARRAY_SIZE是一种数组最大值,详细可以去Integer类中查看
//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);
//}
public class MyArrayList {
private int size;
private Object[] element;
// 无参构造函数
public MyArrayList() {
element = new Object[10];
}
// 有参构造函数
public MyArrayList(int usize) {
if (usize >= 0) {//判断输入参数是否合理
element = new Object[usize];
} else {
try {
throw new Exception("is wrong");
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 判断是否为空
public boolean isEmpty(int size) {
return size == 0;
}
public Object get(int index) {
//判断输入的下标是否合适;
if (index < 0 || index > size) {
try {
throw new Exception("is wrong");
} catch (Exception e) {
e.printStackTrace();
}
}
return element[index];
}
public void add(Object obj) {
//下面是进行扩容,这是我的这个1.8的源码
if (size == element.length) {
Object[] values = new Object[(3 * size)/2 + 1];
System.arraycopy(element, 0, values, 0, element.length);
element = values;
}
element[size++] = obj;
}
public static void main(String[] args) {
MyArrayList ma = new MyArrayList(10);
ma.add("dd");
ma.add("dased");
ma.add("ssd");
ma.add("sdsew");
System.out.println(ma.get(1));
}
}
一个使用过的名为复制的方法:
System.arraycopy(要复制的数组名, 复制的起始位置, 目标数组, 复制到目标数组的位置, 复制的长度);
下方代码是ArrayList的源码set()方法;
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;//为什么返回值是oldValue?以后的学习我一定会知道的
}
2.LinkedList基础使用
下图是一张双向链表,linkedList图,指一个节点含有链接他的其他节点的信息;
可以把一个节点看成三部分,第一部分存它上一个节点位置,中间存放自己的值,最后一部分则存下一个 节点的信息;
第一个节点,第一部分为null,因为它没有上一个节点,最后一个节点则是最后一个部分为null,因为它后面没有节点;
- 这里定义一个节点类,然后主要就是get(),set()方法;
- 如果不记得get,set方法有什么用了,简单来说就是:可以通过get,set方法来改变或取得这个类中的某些私有值。(用private修饰的,无法直接获取的)
- 在这里也可以不用private修饰,如果不用private修饰那么在MylinkedList类中就可以不需要set,get方法,直接可以调用其属性;但是最好还是使用private吧;因为使用private的时候,外部调用该属性时,不能直接使用对象.属性值直接进行修改;而且set,get方法中还可以创建一些条件,阻止一些非法的赋值,比如可以添加一个if语句进行判断,这个值是否可以输入之类的;
//定义的一个节点类;
public class Node {
private Node next;
private Object obj;
private Node previous;
//提供一个空构造器的理由是有时需要创造一个对象,但是不需要太多的参数;如果不设置,那么就只能使用下面的构造器;
public Node() {
}
public Node(Node next, Object obj, Node previous) {
super();
this.next = next;
this.obj = obj;
this.previous = previous;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Node getPrevious() {
return previous;
}
public void setPrevious(Node previous) {
this.previous = previous;
}
}
以下发代码中有部分linkedList的主要方法,为了记忆更加深刻,我照着源码全部学了一遍,方便了解LinkedList的基本结构;除此之外还有一些方法,就不一一执行了,希望我以后看的时候还看的懂,哈哈哈哈哈
public class MyLinkedList {
Node first;
Node last;
private int size;
public int size() {
return size;
}
//添加一个set方法;
public void set(int index,Object obj) {//用于更换一个指定节点的值
checkrange(index);
Node x = node(index);
Object oldnode = x.getObj();
x.setObj(obj);
}
//添加一个add方法,添加一个节点
public void add(Object obj) {
Node node = new Node();
if(first==null) {
node.setPrevious(null);//前节点为null
node.setObj(obj);//赋给节点的值
node.setNext(null);//节点的下一个节点也不知道是谁,所以为null;
first = node;//这是第一个节点,自然这个节点的first就应该赋值给自己,
last = node;//最后一个节点位置也应该赋值给自己
}else {
node.setPrevious(last);
node.setObj(obj);
node.setNext(null);
last.setNext(node);//使得上一个节点的下一个节点时node,就是为了形成一条链。last是指上一个节点;
last = node;//把这个节点赋值为last,因为现在他才是最后一个节点;
}
size++;
}
//用于指定一个判断index是否非法的方法;非法则抛出异常;
private void checkrange(int index) {
if(size<index||index<0) {
try {
throw new Exception("out of bounds");
}catch(Exception e){
e.printStackTrace();
}
}
}
//定义一个get方法,得到一个节点的值
public Object get(int index) {
checkrange(index);
return node(index).getObj();
}
//定义一个方法node用于移除和获得;
public Node node(int index) {
if(index<(size/2)) {
Node node = first;
for(int i=0;i<index;i++) {
node=node.getNext();
}
return node;
}else {
Node node = last;
for(int i=size-1;i>index;i--) {
node=node.getPrevious();
}
return node;
}
}
//定义一个移除所需要的方法unlink
public Node unlink(Node node) {
Node up = node.getPrevious();
Node down = node.getNext();
if(up==null) {
first=down;//这里面first属于一种变量,千万别当成常量看;
}else {
up.setNext(down);
//在这里的时候,由于是链式,所以只需要确定一条链,再取消掉那个要删除的链子和前面链子的链接;就可以重新规划一条链子了;
node.setNext(null);
}
if(down==null) {
last=up;
}else {
down.setPrevious(up);
node.setPrevious(null);
}
size--;
return node;
}
//定义一个移除节点的方法;
public Node remove(int index) {
checkrange(index);
Node temp = node(index);
return unlink(temp);
}
public static void main(String[] args) {
MyLinkedList ml = new MyLinkedList();
ml.add("dsd");
ml.add("ddd");
ml.add("jkdhf");
ml.add("sd");
ml.add("ddd");
ml.set(0, "1111");
// ml.remove(2);
System.out.println(ml.get(2));
System.out.println(ml.get(0));
// System.out.println(ml.get(2));
// System.out.println(ml.get(3));
// System.out.println(ml.get(4));
System.out.println(ml.size());
}
}
虽然在写的时候由于理解不深刻出现了错误,但是写完的时候感觉还是学到了很多,希望继续努力;
明天必须把Map相关的重要子类全部了解一遍,加油!加油!加油!