• Java集合——Collection接口


    Java集合——Collection接口

    摘要:本文主要介绍了Java集合的Collection接口。

    概述

    Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性。Collection包含了List和Set两大分支。

    常用方法

    添加单个元素:boolean add(Object object);

    添加一个集合里的所有元素:boolean addAll(Collection<? extends E> collection);

    删除单个元素:boolean remove(Object object);

    删除指定集合里有的元素:boolean removeAll(Collection collection);

    删除两个集合都有的元素:boolean retainAll(Collection collection);

    判断是否包含某个元素:boolean contains(Object object);

    判断是否包含指定集合的所有元素:boolean containsAll(Collection<?> collection);

    判断集合是否为空:boolean isEmpty();

    清除集合里的元素:void clear();

    获取集合元素个数:int size();

    将集合转换为数组:Object[] toArray();

    将集合转换为指定类型的数组:<T> T[] toArray(T[] array);

    获取集合迭代器:Iterator iterator();

    集合同数组的比较

    数组长度一旦固定,不能再改变,集合的长度是可以改变的。

    数组只能保存相同类型的数据,集合可以保存指定类型或其子类型的数据。

    数组在使用的时候相对比较麻烦,集合可以利用多种方法,还有工具类。

    List接口

    List接口继承自Collection接口,允许定义一个重复的有序集合,集合中的每个元素都有对应的一个索引,可以通过索引访问List中的元素。

    实现List接口的实现类主要有:ArrayList、LinkedList、Vector、Stack。

    特点

    允许重复。

    有序,取出的顺序和插入的顺序一致。

    为每一个元素提供一个索引值,默认从0开始。

    常用方法

    在指定索引位置添加单个元素:void add(int index, Object object);

    在指定索引位置添加一个集合:boolean addAll(int index, Collection<? extends E> collection);

    删除指定位置的单个元素:Object remove(int index);

    获取指定位置的单个元素:Object get(int index);

    替换指定位置的单个元素:Object set(int index, Object object);

    获取指定元素的出现的第一个索引:int indexOf(Object object);

    获取指定元素的出现的最后一个索引:int lastIndexOf(Object object);

    获取指定位置的集合,包含起始位置,不包含结束位置:List<E> subList(int fromIndex, int toIndex);

    获取集合迭代器:ListIterator<E> listIterator();

    ArrayList类

    特点

    ArrayList是动态数组结构,也是我们最常用的集合,允许任何符合规则的元素插入,包括null。

    ArrayList提供了索引机制,可以通过索引迅速查找元素,查找效率高。但是每次增加或删除元素时,身后的元素都要移动,所以增删效率低。

    ArrayList的操作是非同步的,是线程不安全的。

    扩容机制

    数组结构都会有容量的概念,ArrayList的初始容量为10,加载因子是1,当快插入元素后长度超出原有长度时会进行扩增,扩容增量是0.5,扩增后容量为1.5倍,可使用方法手动扩容和缩减。

    如果一开始就明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间和效率。

    属性

     1 // 默认的初始容量为10。
     2 private static final int DEFAULT_CAPACITY = 10;
     3 // 空数组。
     4 private static final Object[] EMPTY_ELEMENTDATA = {};
     5 // 默认容量的空数组。
     6 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
     7 // 数组。
     8 transient Object[] elementData;
     9 // 元素个数。
    10 private int size;

    构造方法

     1 // 空参构造器,返回默认容量为10的集合。
     2 public ArrayList() {
     3     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     4 }
     5 
     6 // 指定长度的构造器,如果长度为0,则返回容量为0的集合。
     7 public ArrayList(int initialCapacity) {
     8     if (initialCapacity > 0) {
     9         this.elementData = new Object[initialCapacity];
    10     } else if (initialCapacity == 0) {
    11         this.elementData = EMPTY_ELEMENTDATA;
    12     } else {
    13         throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
    14     }
    15 }
    16 
    17 // 传入了一个集合的构造器,如果集合长度为0,返回容量为0的集合。
    18 public ArrayList(Collection<? extends E> c) {
    19     elementData = c.toArray();
    20     if ((size = elementData.length) != 0) {
    21         // Object[]数组里的类型不一定都是Object类型的,有可能是Object的子类,所以要判断一下。
    22         if (elementData.getClass() != Object[].class)
    23             elementData = Arrays.copyOf(elementData, size, Object[].class);
    24     } else {
    25         this.elementData = EMPTY_ELEMENTDATA;
    26     }
    27 }

    常用方法

      1 // 对集合进行扩容。
      2 public void ensureCapacity(int minCapacity) {
      3     // 如果集合不为空,则设置扩充值为0,如果为空,则设置扩充值为10。
      4     int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
      5     // 如果指定容量大于扩充值,则进行扩容。
      6     if (minCapacity > minExpand) {
      7         ensureExplicitCapacity(minCapacity);
      8     }
      9 }
     10 
     11 // 对集合进行扩容。
     12 private void ensureCapacityInternal(int minCapacity) {
     13     // 如果集合为空,则在默认大小和最小值之间取最大的。
     14     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     15         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
     16     }
     17     // 进行扩容。
     18     ensureExplicitCapacity(minCapacity);
     19 }
     20 
     21 // 对集合进行扩容,实际操作。
     22 private void ensureExplicitCapacity(int minCapacity) {
     23     // 操作数加一。
     24     modCount++;
     25     // 如果最小值大于数组长度,则进行扩容。
     26     if (minCapacity - elementData.length > 0)
     27         grow(minCapacity);
     28 }
     29 
     30 // 数组作为一个对象,需要一定的内存存储对象头信息,对象头信息最大占用内存不可超过8字节。
     31 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
     32 
     33 // 扩容计算。
     34 private void grow(int minCapacity) {
     35     // 获取原容量大小。
     36     int oldCapacity = elementData.length;
     37     // 右移一位,变为原来的0.5倍,然后加上原大小,扩容后变为1.5倍。
     38     int newCapacity = oldCapacity + (oldCapacity >> 1);
     39     // 如果扩容后小于最小值,则设置容量为最小值。
     40     if (newCapacity - minCapacity < 0)
     41         newCapacity = minCapacity;
     42     // 如果扩容后大于最大值,则进一步设置容量。
     43     if (newCapacity - MAX_ARRAY_SIZE > 0)
     44         newCapacity = hugeCapacity(minCapacity);
     45     // minCapacity is usually close to size, so this is a win:
     46     elementData = Arrays.copyOf(elementData, newCapacity);
     47 }
     48 
     49 // 扩容后容量过大的处理方法。
     50 private static int hugeCapacity(int minCapacity) {
     51     // 如果最小值小于零,则表示溢出,抛出内存溢出异常。
     52     if (minCapacity < 0)
     53         throw new OutOfMemoryError();
     54     // 如果扩容后的值大于最大值,则使用Integer的最大值,否则就使用最大值。
     55     return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
     56 }
     57 
     58 // 获取指定下标的元素。
     59 public E get(int index) {
     60     // 检查指定下标是否越界。
     61     rangeCheck(index);
     62     // 返回指定下标的元素。
     63     return elementData(index);
     64 }
     65 
     66 // 设置指定下标的指定元素,并返回旧元素。
     67 public E set(int index, E element) {
     68     // 检查指定下标是否越界。
     69     rangeCheck(index);
     70     // 设置指定元素并返回旧元素。
     71     E oldValue = elementData(index);
     72     elementData[index] = element;
     73     return oldValue;
     74 }
     75 
     76 // 添加元素,并返回是否成功。
     77 public boolean add(E e) {
     78     // 扩容并增加操作数。
     79     ensureCapacityInternal(size + 1);
     80     // 元素个数增加并设置指定元素。
     81     elementData[size++] = e;
     82     // 返回成功。
     83     return true;
     84 }
     85 
     86 // 在指定位置添加指定元素。
     87 public void add(int index, E element) {
     88     // 检查指定下标是否越界。
     89     rangeCheckForAdd(index);
     90     // 扩容并增加操作数。
     91     ensureCapacityInternal(size + 1);
     92     // 拷贝数组并设置元素,增加元素个数。
     93     System.arraycopy(elementData, index, elementData, index + 1, size - index);
     94     elementData[index] = element;
     95     size++;
     96 }
     97 
     98 // 删除指定位置的元素,并返回删除的元素。
     99 public E remove(int index) {
    100     // 检查指定下标是否越界。
    101     rangeCheck(index);
    102     // 增加操作数。
    103     modCount++;
    104     // 获取指定下标的元素。
    105     E oldValue = elementData(index);
    106     // 需要移动的元素个数。
    107     int numMoved = size - index - 1;
    108     if (numMoved > 0)
    109         System.arraycopy(elementData, index+1, elementData, index, numMoved);
    110     // 将最后一个元素设置为null并减少容量大小。
    111     elementData[--size] = null;
    112     // 返回指定下标的元素。
    113     return oldValue;
    114 }
    115 
    116 // 删除第一次出现的指定元素。
    117 public boolean remove(Object o) {
    118     // 如果指定元素是null。
    119     if (o == null) {
    120         for (int index = 0; index < size; index++)
    121             if (elementData[index] == null) {
    122                 fastRemove(index);
    123                 return true;
    124             }
    125     } else {
    126         for (int index = 0; index < size; index++)
    127             if (o.equals(elementData[index])) {
    128                 fastRemove(index);
    129                 return true;
    130             }
    131     }
    132     return false;
    133 }

    遍历方法

    ArrayList提供了两种迭代器,实现了Iterator接口的Itr类,以及实现了ListIterator接口并继承了Itr类的ListItr类。

    ListItr类对Itr类的方法进行了扩展,提供了添加和修改的方法,这里只学习Itr类。

      1 // 获取Iterator迭代器。
      2 public Iterator<E> iterator() {
      3     return new Itr();
      4 }
      5 
      6 // Iterator内部类,实现了Iterator接口。
      7 private class Itr implements Iterator<E> {
      8     // 下一个元素的位置。
      9     int cursor;
     10     // 最后一个元素的位置。
     11     int lastRet = -1;
     12     // 预期的操作数。
     13     int expectedModCount = modCount;
     14 
     15     // 是否存在下一个元素。
     16     public boolean hasNext() {
     17         return cursor != size;
     18     }
     19 
     20     // 获取下一个元素。
     21     @SuppressWarnings("unchecked")
     22     public E next() {
     23         // 校验预期操作数和实际操作数。
     24         checkForComodification();
     25         // 将下一个元素的位置赋值给i。
     26         int i = cursor;
     27         // 如果i大于或等于集合大小,说明没有元素了,抛出异常。
     28         if (i >= size)
     29             throw new NoSuchElementException();
     30         // 获取集合数组。
     31         Object[] elementData = ArrayList.this.elementData;
     32         // 如果i大于或等于数组长度,说明有其他线程改过了,抛出异常。
     33         if (i >= elementData.length)
     34             throw new ConcurrentModificationException();
     35         // 下一个元素位置加一。
     36         cursor = i + 1;
     37         // 获取元素并返回,设置最后一个元素位置。
     38         return (E) elementData[lastRet = i];
     39     }
     40 
     41     // 删除当前元素。
     42     public void remove() {
     43         // 如果当前位置小于零,抛出异常。
     44         if (lastRet < 0)
     45             throw new IllegalStateException();
     46         // 校验预期操作数和实际操作数。
     47         checkForComodification();
     48         try {
     49             // 移除当前位置上的元素。
     50             ArrayList.this.remove(lastRet);
     51             // 将当前位置赋值给下一个元素位置。
     52             cursor = lastRet;
     53             // 将当前位置设置为-1,表示没有此位置。
     54             lastRet = -1;
     55             // 同步预期操作数和操作数。
     56             expectedModCount = modCount;
     57         } catch (IndexOutOfBoundsException ex) {
     58             throw new ConcurrentModificationException();
     59         }
     60     }
     61 
     62     // 循环遍历一次,JDK1.8新增方法。
     63     @Override
     64     @SuppressWarnings("unchecked")
     65     public void forEachRemaining(Consumer<? super E> consumer) {
     66         // 判断非空。
     67         Objects.requireNonNull(consumer);
     68         // 集合容量大小。
     69         final int size = ArrayList.this.size;
     70         // 将下一个元素的位置赋值给i。
     71         int i = cursor;
     72         // 如果i大于或等于集合长度,则返回。
     73         if (i >= size) {
     74             return;
     75         }
     76         // 获取集合数组。
     77         final Object[] elementData = ArrayList.this.elementData;
     78         // 如果i大于或等于数组长度,说明有其他线程改过了,抛出异常。
     79         if (i >= elementData.length) {
     80             throw new ConcurrentModificationException();
     81         }
     82         // 循环遍历,当下一个元素不为集合的大小,并且预期操作数和实际操作数相等。
     83         while (i != size && modCount == expectedModCount) {
     84             // 执行方法,并且每次循环i加一。
     85             consumer.accept((E) elementData[i++]);
     86         }
     87         // 将循环后的i赋值,导致下个元素的位置和集合长度相等,该迭代器不能再次遍历集合了。
     88         cursor = i;
     89         // 设置最后一个元素位置。
     90         lastRet = i - 1;
     91         // 校验预期操作数和实际操作数。
     92         checkForComodification();
     93     }
     94 
     95     // 校验预期操作数和实际操作数。
     96     final void checkForComodification() {
     97         if (modCount != expectedModCount)
     98             throw new ConcurrentModificationException();
     99     }
    100 }

    Vector类

    特点

    与ArrayList相似,它的操作与ArrayList几乎一样。

    Vector是同步的,是线程安全的动态数组,但是效率低。

    扩容机制

    初识容量为10,加载因子为1,扩容增量是1,扩增后容量为原来长度的2倍,适用于数据量大的环境。

    LinkedList类

    特点

    LinkedList是双向链表结构,额外提供了操作列表首尾元素的方法,因为不是数组结构,所以不存在扩容机制。

    LinkedList使用了链表结构,通过修改前后两个元素的链接指向实现增加和删除操作,增删效率高,但是查找操作必须从开头或者结尾便利整个列表,所以查找效率低。

    LinkedList的操作是非同步的,是线程不安全的。

    插入方法

     1 // 在首部位置插入元素。
     2 public void push(E e) {
     3     addFirst(e);
     4 }
     5     
     6 // 在尾部位置插入元素。
     7 public boolean offer(E e) {
     8     return add(e);
     9 }
    10 
    11 // 在首部位置插入元素。
    12 public boolean offerFirst(E e) {
    13     addFirst(e);
    14     return true;
    15 }
    16 
    17 // 在尾部位置插入元素。
    18 public boolean offerLast(E e) {
    19     addLast(e);
    20     return true;
    21 }
    22 
    23 // 在指定位置插入元素。
    24 public void add(int index, E element) {
    25     checkPositionIndex(index);
    26 
    27     if (index == size)
    28         linkLast(element);
    29     else
    30         linkBefore(element, node(index));
    31 }
    32 
    33 // 在尾部位置插入元素。
    34 public boolean add(E e) {
    35     linkLast(e);
    36     return true;
    37 }
    38 
    39 // 在首部位置插入元素。
    40 public void addFirst(E e) {
    41     linkFirst(e);
    42 }
    43 
    44 // 在尾部位置插入元素。
    45 public void addLast(E e) {
    46     linkLast(e);
    47 }
    48 
    49 // 在首部位置插入元素。
    50 private void linkFirst(E e) {
    51     final Node<E> f = first;
    52     final Node<E> newNode = new Node<>(null, e, f);
    53     first = newNode;
    54     if (f == null)
    55         last = newNode;
    56     else
    57         f.prev = newNode;
    58     size++;
    59     modCount++;
    60 }
    61 
    62 // 在尾部位置插入元素。
    63 void linkLast(E e) {
    64     final Node<E> l = last;
    65     final Node<E> newNode = new Node<>(l, e, null);
    66     last = newNode;
    67     if (l == null)
    68         first = newNode;
    69     else
    70         l.next = newNode;
    71     size++;
    72     modCount++;
    73 }
    74 
    75 // 在指定元素前插入元素。
    76 void linkBefore(E e, Node<E> succ) {
    77     // 断言指定节点不为null。
    78     final Node<E> pred = succ.prev;
    79     final Node<E> newNode = new Node<>(pred, e, succ);
    80     succ.prev = newNode;
    81     if (pred == null)
    82         first = newNode;
    83     else
    84         pred.next = newNode;
    85     size++;
    86     modCount++;
    87 }

    删除方法

      1 // 删除首部位置元素,并返回删除的元素。
      2 public E pop() {
      3     return removeFirst();
      4 }
      5 
      6 // 删除首部位置元素,并返回删除的元素。
      7 public E poll() {
      8     final Node<E> f = first;
      9     return (f == null) ? null : unlinkFirst(f);
     10 }
     11 
     12 // 删除首部位置元素,并返回删除的元素。
     13 public E pollFirst() {
     14     final Node<E> f = first;
     15     return (f == null) ? null : unlinkFirst(f);
     16 }
     17 
     18 // 删除尾部位置元素,并返回删除的元素。
     19 public E pollLast() {
     20     final Node<E> l = last;
     21     return (l == null) ? null : unlinkLast(l);
     22 }
     23 
     24 // 删除首部位置元素,并返回删除的元素。
     25 public E remove() {
     26     return removeFirst();
     27 }
     28 
     29 // 删除指定位置元素,并返回删除的元素。
     30 public E remove(int index) {
     31     checkElementIndex(index);
     32     return unlink(node(index));
     33 }
     34 
     35 // 删除指定元素。
     36 public boolean remove(Object o) {
     37     if (o == null) {
     38         for (Node<E> x = first; x != null; x = x.next) {
     39             if (x.item == null) {
     40                 unlink(x);
     41                 return true;
     42             }
     43         }
     44     } else {
     45         for (Node<E> x = first; x != null; x = x.next) {
     46             if (o.equals(x.item)) {
     47                 unlink(x);
     48                 return true;
     49             }
     50         }
     51     }
     52     return false;
     53 }
     54 
     55 // 删除首部位置元素,并返回删除的元素。
     56 public E removeFirst() {
     57     final Node<E> f = first;
     58     if (f == null)
     59         throw new NoSuchElementException();
     60     return unlinkFirst(f);
     61 }
     62 
     63 // 删除尾部位置元素,并返回删除的元素。
     64 public E removeLast() {
     65     final Node<E> l = last;
     66     if (l == null)
     67         throw new NoSuchElementException();
     68     return unlinkLast(l);
     69 }
     70 
     71 // 删除首部位置元素,并返回删除的元素。
     72 private E unlinkFirst(Node<E> f) {
     73     final E element = f.item;
     74     final Node<E> next = f.next;
     75     f.item = null;
     76     f.next = null; // help GC
     77     first = next;
     78     if (next == null)
     79         last = null;
     80     else
     81         next.prev = null;
     82     size--;
     83     modCount++;
     84     return element;
     85 }
     86 
     87 // 删除尾部位置元素,并返回删除的元素。
     88 private E unlinkLast(Node<E> l) {
     89     // assert l == last && l != null;
     90     final E element = l.item;
     91     final Node<E> prev = l.prev;
     92     l.item = null;
     93     l.prev = null; // help GC
     94     last = prev;
     95     if (prev == null)
     96         first = null;
     97     else
     98         prev.next = null;
     99     size--;
    100     modCount++;
    101     return element;
    102 }
    103 
    104 // 删除指定节点元素,并返回删除的元素。
    105 E unlink(Node<E> x) {
    106     // 断言指定节点不为null。
    107     final E element = x.item;
    108     final Node<E> next = x.next;
    109     final Node<E> prev = x.prev;
    110 
    111     if (prev == null) {
    112         first = next;
    113     } else {
    114         prev.next = next;
    115         x.prev = null;
    116     }
    117 
    118     if (next == null) {
    119         last = prev;
    120     } else {
    121         next.prev = prev;
    122         x.next = null;
    123     }
    124 
    125     x.item = null;
    126     size--;
    127     modCount++;
    128     return element;
    129 }

    获取方法

     1 // 获取首部节点。
     2 public E peek() {
     3     final Node<E> f = first;
     4     return (f == null) ? null : f.item;
     5 }
     6 
     7 // 获取首部节点。
     8 public E peekFirst() {
     9     final Node<E> f = first;
    10     return (f == null) ? null : f.item;
    11 }
    12 
    13 // 获取尾部节点。
    14 public E peekLast() {
    15     final Node<E> l = last;
    16     return (l == null) ? null : l.item;
    17 }
    18 
    19 // 获取指定位置的元素。
    20 public E get(int index) {
    21     checkElementIndex(index);
    22     return node(index).item;
    23 }
    24 
    25 // 获取首部节点。
    26 public E getFirst() {
    27     final Node<E> f = first;
    28     if (f == null)
    29         throw new NoSuchElementException();
    30     return f.item;
    31 }
    32 
    33 // 获取尾部节点。
    34 public E getLast() {
    35     final Node<E> l = last;
    36     if (l == null)
    37         throw new NoSuchElementException();
    38     return l.item;
    39 }

    遍历方法

    LinkedList同样支持两种迭代器。

    LinkedList支持多种遍历方式,建议不要采用随机访问的方式去遍历LinkedList,而采用逐个遍历的方式。

     1 // 通过迭代器遍历。
     2 for (Iterator<String> iter = list.iterator(); iter.hasNext();) {
     3     System.out.println(iter.next());
     4 }
     5 
     6 // 通过普通for循环遍历,不建议使用这种方式遍历。
     7 for (int i = 0; i < list.size(); i++) {
     8     System.out.println(list.get(i));
     9 }
    10 
    11 // 通过增强for循环来遍历。
    12 for (String string : list) {
    13     System.out.println(string);
    14 }
    15 
    16 // 通过removeFirst()方法来遍历,建议使用这种方式遍历,但是要注意可能会抛出异常。
    17 String string;
    18 try {
    19     while ((string = list.removeFirst()) != null) {
    20         System.out.println(string);
    21     }
    22 } catch (NoSuchElementException e) {
    23     e.printStackTrace();
    24 }

    Set接口

    Set接口继承自Collection接口,允许定义一个不重复的无序集合,集合中只允许存在一个null值。

    实现Set接口的实现类主要有:HashSet、LinkedHashSet、TreeSet。

    特点

    不可以重复,只能插入一个空值。

    无序,不能保证插入的顺序和输出的顺序一致。

    没有索引。

    HashSet类

    特点

    HashSet的底层是HashMap。

    HashSet使用了一个散列集存储数据,通过元素的Hash值进行排序,不能保证插入和输出的顺序一致。

    HashSet不能插入重复的元素,只能存在一个null值。

    HashSet内部通过哈希表进行排序,具有很好的查找和存取功能。

    HashSet是非同步的,线程不安全。

    扩容机制

    和HashMap相同。

    LinkedHashSet类

    特点

    LinkedHashSet继承自HashSet,其底层是基于LinkedHashMap来实现的。

    LinkedHashSet使用链表维护元素的次序,同时根据元素的hash值来决定元素的存储位置,遍历集合时候,会以元素的添加顺序访问集合的元素。

    LinkedHashSet不能插入重复的元素,只能存在一个null值。

    LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。

    LinkedHashSet是非同步的,线程不安全。

    扩容机制

    和HashMap相同。

    TreeSet类

    特点

    TreeSet的底层是TreeMap。

    TreeSet基于二叉树结构,可以实现自然排序。

    TreeSet通过比较方法的返回值来判断元素是否相等,因此不能添加null的数据,不能添加重复元素,只能插入同一类型的数据。

    TreeSet支持两种排序方式,自然排序和定制排序。

    自动排序:添加自定义对象的时候,必须要实现Comparable接口,并要覆盖compareTo方法来自定义比较规则。

    定制排序:创建TreeSet对象时,传入Comparator接口的实现类。要求Comparator接口的compare方法的返回值和两个元素的equals方法具有一致的返回值。

  • 相关阅读:
    hadoop2.2.0+hive-0.10.0完全分布式安装方法
    linux之vim编辑器
    hive与hbase的区别与联系
    linux系统管理
    Hive 自定义函数(转)
    hive 存储格式
    ActiveMQ 使用spring模板 发布消息过程分析
    ActiveMQ spring (一)
    ActiveMQ 权限(二)
    ActiveMQ 权限(一)
  • 原文地址:https://www.cnblogs.com/shamao/p/11013371.html
Copyright © 2020-2023  润新知