[数据结构] - 数组

1、Java数组介绍

  在Java中,数组是用来存放同一种数据类型的集合,注意只能存放同一种数据类型(Object类型数组除外)。

  • 在内存中,数组是一块连续的区域。 拿上面的看电影来说,这几个人在电影院必须坐在一起。

  • 数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间。 比如看电影时,为了保证10个人能坐在一起,必须提前订好10个连续的位置。这样的好处就是能保证10个人可以在一起。但是这样的缺点是,如果来的人不够10个,那么剩下的位置就浪费了。如果临时有多来了个人,那么10个就不够用了,这时可能需要将第11个位置上的人挪走,或者是他们11个人重新去找一个11连坐的位置,效率都很低。如果没有找到符合要求的作为,那么就没法坐了。
  • 插入数据和删除数据效率低,插入数据时,这个位置后面的数据在内存中都要向后移。删除数据时,这个数据后面的数据都要往前移动。 比如原来去了5个人,然后后来又去了一个人要坐在第三个位置上,那么第三个到第五个都要往后移动一个位子,将第三个位置留给新来的人。 当这个人走了的时候,因为他们要连在一起的,所以他后面几个人要往前移动一个位置,把这个空位补上。
  • 随机读取效率很高。因为数组是连续的,知道每一个数据的内存地址,可以直接找到给地址的数据。
  • 并且不利于扩展,数组定义的空间不够时要重新定义数组。一种解决办法是封装一次数组,数组元素超过75%时候就动态扩容,新申请一个1.5倍的数组,把原来的数组全部拷贝过去。

①、数组的声明

第一种方式:

数据类型 []  数组名称 = new 数据类型[数组长度];

这里 [] 可以放在数组名称的前面,也可以放在数组名称的后面,我们推荐放在数组名称的前面,这样看上去 数据类型 [] 表示的很明显是一个数组类型,而放在数组名称后面,则不是那么直观。

第二种方式:

数据类型 [] 数组名称 = {数组元素1,数组元素2,......}

 这种方式声明数组的同时直接给定了数组的元素,数组的大小由给定的数组元素个数决定。

//声明数组1,声明一个长度为3,只能存放int类型的数据
int [] myArray = new int[3];
//声明数组2,声明一个数组元素为 1,2,3的int类型数组
int [] myArray2 = {1,2,3};

②、访问数组元素以及给数组元素赋值

  数组是存在下标索引的,通过下标可以获取指定位置的元素,数组小标是从0开始的,也就是说下标0对应的就是数组中第1个元素,可以很方便的对数组中的元素进行存取操作。

  前面数组的声明第二种方式,我们在声明数组的同时,也进行了初始化赋值。
  
 

//声明数组,声明一个长度为3,只能存放int类型的数据
int [] myArray = new int[3];
//给myArray第一个元素赋值1
myArray[0] = 1;
//访问myArray的第一个元素
System.out.println(myArray[0]);

上面的myArray 数组,我们只能赋值三个元素,也就是下标从0到2,如果你访问 myArray[3] ,那么会报数组下标越界异常。

③、数组遍历

  数组有个 length 属性,是记录数组的长度的,我们可以利用length属性来遍历数组。
  

//声明数组2,声明一个数组元素为 1,2,3的int类型数组
int [] myArray2 = {1,2,3};
for(int i = 0 ; i < myArray2.length ; i++){
    System.out.println(myArray2[i]);
}

2、简单封装一个动态数组

package me.liangtian.array;
 
public class Array<E> {
    /**
     * 存放数据的数组
     */
    private E[] data;
    /**
     * 数组中现有数据量
     */
    private int size;
    
    public Array(int capacity) {
        data = (E[])new Object[capacity];
        size = 0;
    }
    /**
     * 默认数组长度10
     */
    public Array() {
        new Array<>(10);
    }
    /**
     * 得到数组长度
     */
    public int getCapacity() {
        return data.length;
    }
    /**
     * 已有数组大小
     */
    public int getSize() {
        return size;
    }
    /**
     * 判断数组是否为空
     */
    public boolean isEmpty() {
        return size == 0;
    }
    /**
     * 索引index处添加一个元素
     * 1.判断索引是否有效
     * 2.判断数组是否已经满了,若满了那么扩容
     * 3.从后往前,index处的元素后移一个位置
     * 4.index索引处元素赋值
     * 5.size++
     */
    public void add(int index, E e) {
        if(index < 0 || index > size) {
            throw new IllegalArgumentException("add failed, index must between 0 and size");
        }
        if(index == size) {
            resize( 2 * data.length);
        }
        for (int i = size -1; i >= index; i--) {
            data[i+1] = data[i];
        }
        data[index] = e;
        size++;
    }
    /**
     * 在尾部添加新元素
     */
    public void addLast(E e) {
        add(size, e);
    }
    /**
     * 在头部添加新元素
     */
    public void addFirst(E e) {
        add(0, e);
    }
    
    /**
     * 是否包含否个元素
     */
    public boolean contains(E e) {
        for (E e1 : data) {
            if(e1.equals(e)) {
                return true;
            }
        }
        return false;
    } 
    
    /**
     * 动态扩容
     * 1.新建一个容器,大小为指定大小newCapacity
     * 2.将之前的容器的元素按原有顺序放到新的容器中
     * 3.将原指针指向新容器
     */
    private void resize(int newCapacity) {
        E[] newDate = (E[]) new Object[newCapacity];
        for(int i = 0; i < size; i++) {
            newDate[i] = data[i];
        }
        data = newDate;
    }
    /**
     * 查找元素E在数组中的索引
     * 1.遍历所有的数组,若匹配到(equals非==)那么返回索引,否则返回-1
     */
    private int find(E e) {
        for (int i = 0; i < data.length; i++) {
            if(data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }
    
    /**
     * 删除元素并返回删除之前的位置
     * 若不存在则返回-1
     */
    public int removeElement(E e) {
        int index = find(e);
        if(index != -1) {
            remove(index);
        }
        return index;
    }
    /**
     * 根据索引删除元素,并返回删除元素
     * 1.判断索引是否有效
     * 2.将索引处的元素保存到单独一个变量,用于返回
     * 3.将所有元素左移,索引从小到大
     * 4.将size索引处的元素清空,并且size-1
     * 5.均摊算法复杂度。为防止算法复杂度振荡,只有size <= data.leng/4 那么重新调整数组大小为原大小二分之一
     *   注意数组容器大小不能为0
     */
    private E remove(int index) {
        if(index < 0 || index >= size) {
            throw new IllegalArgumentException("index must between 0 and size -1");
        }
        E removeData = data[index];
        for(int i = index + 1; i < size; i++) {
            data[i-1] = data[i];
        }
        data[index] = null;
        size--;
        if(size <= data.length/4 && data.length /2 != 0) {
            resize(data.length /2);
        }
        return removeData;
    }
}
posted @ 2019-01-14 11:14 梁天 阅读(...) 评论(...) 编辑 收藏