java核心-简单的数据结构

###.java核心内容

1.集合

2.泛型

3.反射

4.注解

1.数据结构

1.数组

数组的三种定义方式,和数组容易出现的异常,空指针的数组下标越界

特点:

内存地址连续,使用之前必须要指定长度

可以通过下标访问的方式访问资源,查询效率比较高,

增删效率低,需要复制数组,源码可见

public boolean add(E e) {    
    ensureCapacityInternal(size + 1);  
    elementData[size++] = e;   
    return true;}

可见,在增加操作的时候,会先对数组的大小扩容判断,然后再创建一个比原来容器大小加1的数组存放数据

2.链表

单向链表和双向链表

双向链表

特点:

1.灵活的空间要求,没有要求存储空间的地址连续

2.不能通过下标访问,只能顺序便利,一个一个的读取

3.因为是连续的,对于头尾的数据增删效率比较高

数据结构
private static class Node<E> {
        E item;//节点中的元素
        Node<E> next;//下一个节点
        Node<E> prev;//上一个节点

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

3.树

平衡二叉树

特点:

数据按照从低到高,从左到右依次排列

左节点深度和右节点深度一样

红黑树

1.根节点必须是黑色

2.每个叶子节点是黑色(其中每个叶子节点包含两个空节点,一般未显示)

3.红色节点的两个子节点必须是黑色

4.任意根节点到每个叶子节点的路劲包含相同数量的黑节点

是一个黑平衡二叉树

1.recolor 重新标志节点为红色或者黑色

2.rotation旋转实现平衡(树达到平衡的关键)

首先约定插入的新节点的颜色都为红色。然后将该节点插入的按二叉查找树的规则插入到树中。这个节点后文称为N

\1. 根节点为空。这种情况,将N的颜色改为黑色即可。

\2. N的父节点为黑色。这种情况不需要做修改。

\3. N的父节点为红色(根据性质3,N的祖父节点必为黑色)。

(1)N的叔父节点为红色。这种情况,将N的父节点和叔父节点的颜色都改为黑色,若祖父节点是跟节点就将其改为黑色,否则将其颜色改为红色,并以祖父节点为插入的目标节点从情况1开始递归检测。

  • img

    (2)N的叔父节点为黑色, 且N和N的父节点在同一边(即父节点为祖父的左儿子时,N也是父节点的左儿子。父节点为祖父节点的右儿子时。N也是父节点的右儿子)。以父节点为祖父节的左儿子为例,将父节点改为黑色,祖父节点改为红色,然后以祖父节点为基准右旋。(N为父节点右儿子时做相应的左旋。)

    img

    (3)N的叔父节点为黑色,切N和N的父节点不在同一边(即父节点为祖父的左儿子时,N是父节点的右儿子。父节点为祖父节点的右儿子时。N也是父节点左右儿子)。以父节点为祖父节点的左儿子为例。以父节点为基准,进行左旋,然后以父节点为目标插入节点进入情况3的b情况进行操作

img

2.集合

Collection接口

map接口

iterator迭代器

工具类:

collections

Arrays

比较器

comparable compartor

ArrayList数组源码分析
构造方法:
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
有参构造
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
add方法:
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//return  1 or 10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //操作次数,自增

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            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);
        //复制数组和容器大小,返回一个新的满足条件的数组
    }

上面一段代码建议自己阅读源码,根据方法名称了解每个方法的作用

get方法
public E get(int index) {
//判断下标是否越界
        rangeCheck(index);
//通过下标返回对应数据
        return elementData(index);
    }

private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

set方法
public E set(int index, E element) {
        rangeCheck(index);
        //获取下标原来的值,赋值新的值,返回原来的值
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

remove方法
public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);
        //获取要移动的元素个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
        //操作原来的数组,将需要移除的下标之后的数据,整体复制替换掉从要移除下标开始的后面的数据,并将最后一个下标位置的数据置换为Null
        例如:
        {1,2,3,4,5}
        这里移除掉第三个位置的数据
        {1,2,4,5,null}
        变换成这种形式
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

linkedList

通过双向链表实现,数据结构具有双向链表的优缺点,既然是双向链表,他的顺序访问效率很高,随机访问效率比较低,包含一个非常重要的私有内部静态内:Node

 private static class Node<E> {
        E item;//节点元素
        Node<E> next;//下一个节点
        Node<E> prev;//上一个节点

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

push方法:
list2.push("1");

private void linkFirst(E e) {
        final Node<E> f = first;
        //创建一个新的节点,e表示元素,null表示头结点,f表示尾节点(上一次插入前获取的头结点)
        final Node<E> newNode = new Node<>(null, e, f);
        //将新创建的节点赋值给第一个节点
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

add方法
 list2.add("2");

void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

get方法
 list2.get(1);

public E get(int index) {
//检查Index是否合法
        checkElementIndex(index);
        //返回索引位置的元素(内部涉及便利操作)
        return node(index).item;
    }

set方法
list2.set(1,"4");

public E set(int index, E element) {
        checkElementIndex(index);
        //获取到原来索引的值并返回
        Node<E> x = node(index);
        E oldVal = x.item;
        //将新的值赋值给索引位置的节点
        x.item = element;
        return oldVal;
    }

Vector

和ArrayList很类似,都是以动态数组的形式来存储数据,Vector是线程安全的

因为每个方法都加synchronized锁,性能较差

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

使用Collections工具类替换

List<String> stringList = Collections.synchronizedList(list);

public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

本质上通过添加如下代码,实现对list操作的同步安全

 final List<E> list;
  public E get(int index) {
            synchronized (mutex) {return list.get(index);}
        }
        public E set(int index, E element) {
            synchronized (mutex) {return list.set(index, element);}
        }
        public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E remove(int index) {
            synchronized (mutex) {return list.remove(index);}
        }

Set接口
1.HashSet

HashSet实现了set接口,由哈希表支持,不保证set的迭代顺序,但是保证数据有序,可以为Null

HashSet hashSet = new HashSet();

public HashSet() {
        map = new HashMap<>();
    }

hashset本质上是一个hashMap

add方法
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
 private static final Object PRESENT = new Object();   

本质上是将数据保存在hashmap中,key是我们添加的内从容,value及时我们定义的一个object对象,底层数据结构是哈希表,hashset的本质是一个没有重复元素的集合。

2.treeset

基于treemap的实现,使用元素的自然顺序对元素进行排序,

 TreeSet treeSet = new TreeSet();

public TreeSet() {
        this(new TreeMap<E,Object>());
    }

public TreeMap() {
        comparator = null;
    }

 private transient NavigableMap<E,Object> m;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

add方法
public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

本质上是将数据保存在treeMap中,key是我们添加到内容,value是我们定义的object对象。

Map接口

map集合的特点:

1.能够存储唯一列的数据

2.可以存储可以重复的数据

3.值得顺序取决于键的顺序

4.键和值都是可以存储null元素

TreeMap

本质上是一个红黑树的实现,红黑树特点具体见上面

HashMap
 TreeMap treeMap = new TreeMap();

 //treemap中存在root成员变量,其本质就是一个红黑树
 private transient Entry<K,V> root;
 
   K key;
        V value;
        Entry<K,V> left;//左子节点
        Entry<K,V> right;//右子节点
        Entry<K,V> parent;//父节点
        boolean color = BLACK;//默认黑色

put方法:

treeMap.put(1,"2");

源码如下:

public V put(K key, V value) {
//根节点赋值给t
        Entry<K,V> t = root;
        //判断初始化第一次插入,比较值得大小
        if (t == null) {
            compare(key, key); // type (and possibly null) check
            //创建一个entry对象并复制给root根节点
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        //不是第一次插入
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
        //开始循环
            do {
                parent = t;//将root根节点赋值给parent
                cmp = cpr.compare(key, t.key);//比较两个值得大小(key决定了元素存放的位置,后再hashmap中会讲到)
                if (cmp < 0)
                    t = t.left;//如果key小于根节点的key,存放在左侧
                else if (cmp > 0)
                    t = t.right;//如果key大于根节点的key,存放在右侧
                else
                    return t.setValue(value);//否则覆盖当前位置的值
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
                //同上
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //创建一个entry对象,将父节点的值赋值给e
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;//放在父节点左侧
        else
            parent.right = e;//放在父节点右侧
        fixAfterInsertion(e);//调整红黑树的高度,使其相对平衡
        size++;
        modCount++;
        return null;
    }

红黑树平衡方法:fixAfterInsertion
private void fixAfterInsertion(Entry<K,V> x) {
//插入节点默认给红色
        x.color = RED;

//判断插入节点不为空,不为根节点,颜色不为黑色
        while (x != null && x != root && x.parent.color == RED) {
        //如果父节点在祖父节点的左侧
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                如果父节点是红色
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    //直接插入,并将祖父节点设置为插入节点,继续判断
                    x = parentOf(parentOf(x));
                } else {
                //如果插入节点再父节点的右侧
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        需要插入后左旋
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    //再右旋
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
             //如果父节点在祖父节点的右侧
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                //如果父节点是红色,一次类推
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值