数据结构与算法(集合)

75 阅读3分钟

集合使用List、Map、Set

List接口、Set接口继承自Collection接口,Map接口无继承父接口

  • 线程安全:HashTable、ConcurrentHashMap、Vector、StringBuffer
  • 非线程安全:LinkedList、ArrayList、HashSet、HashMap、TreeMap、StringBuilder

List、Set、Map初识

List

  • 有序
  • 可重复
  • 支持下标,for循环,迭代器遍历
  • 长度可动态增长
  • 查找元素效率高
  • 插入删除效率低(会引起其他元素位置改变)

Set

  • 无序(集合中元素由HashCode决定顺序、集合中对象需重写equals())
  • 不可重复(重复被覆盖)
  • 仅支持迭代器遍历(无序,无法用下标获取值)
  • 查找元素效率低
  • 插入删除效率高(不会引起其他元素位置改变)

Map

  • 存储键值对数据

List、Set、Map使用

List

ArrayList使用

    1. 基于动态数组的数据结构,因地址连续索引取值效率高,但会导致插入和删除效率低(需移动数据)
# ArrayList.kt
# 索引取值速度快
fun get(index: Int): E {
    if (index >= size)
        throw IndexOutOfBoundsException(outOfBoundsMsg(index))

    return elementData[index] as E
}

fun set(index: Int, element: E): E {
    if (index >= size)
        throw IndexOutOfBoundsException(outOfBoundsMsg(index))

    val oldValue = elementData[index] as E
    elementData[index] = element
    return oldValue
}

# 插入会先判断是否需扩容,中间插值会涉及其他元素操作,将其后元素偏移1位,效率自然降低
fun add(index: Int, element: E) {
    if (index > size || index < 0)
        throw IndexOutOfBoundsException(outOfBoundsMsg(index))

    // Increments modCount!!
    ensureCapacityInternal(size + 1)
    System.arraycopy(elementData, index, elementData, index + 1, size - index)
    elementData[index] = element
    size++
}

# 删除元素需将其后元素向前偏移1位,效率自然降低
fun remove(index: Int): E {
    if (index >= size)
        throw IndexOutOfBoundsException(outOfBoundsMsg(index))

    modCount++
    E oldValue = (E) elementData[index]

    int numMoved = size - index - 1
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved)

    // clear to let GC do its work
    elementData[--size] = null

    return oldValue
}
    1. 支持快速随机查找
# ArrayList.kt
class ArrayList<E> : AbstractList<E>, List<E>, RandomAccess, Cloneable, java.io.Serializable {
    // RandomAccess是支持快速随机访问的标记
}

# Collections.kt
# ArrayList的indexOf和lastIndexOf分别是从头和尾遍历查找,显然比Collections中二分查找效率低
fun <T> binarySearch(list: List<Comparable<T>?>, key: T): Int {
    return if (list is RandomAccess || list.size < Collections.BINARYSEARCH_THRESHOLD)
        Collections.indexedBinarySearch<T>(list, key)
    else
        Collections.iteratorBinarySearch<T>(list, key)
}

LinkedList使用

  • LinkedList基于链表的数据结构,因地址任意,所以开辟内存空间无需等连续地址,相应的插入和删除效率高,但查询效率低(需移动指针)

Vector使用

  • Vector基于数组的数据结构,是线程安全的,通过synchronized修饰实现,线程同步,效率比ArrayList稍差,所以很少使用;同样采用连续空间存储元素,但扩容方式与ArrayList不同(Vector可设置增长因子,ArrayList不可以)

3种List使用对比

  • 需对数据进行多次增删改操作时采用LinkedList
  • 对数据访问较高场景采用ArrayList
  • 不考虑线程安全场景ArrayList比Vector效率高

Set

HashSet、TreeSet使用

  • HashSet是基于哈希表的数据结构,数据无序,可存入null(只能放一个,不可重复),放入对象需实现hashCode()(是以hashCode作为标识),因具有相同内容的String对象hashCode一致,所以不可重复,但同一个类对象可放入不同实例
  • TreeSet是基于二叉树(红黑树)的数据结构,数据自动排序,不可存入null值
  • HashSet基于Hash算法实现,性能优于TreeSet,无需排序功能优先使用HashSet

Map

HashMap、TreeMap、HashTable、ConcurrentHashMap使用

  • HashMap基于哈希表(散列表)的数据结构,添加的键类需重写hashCode()和equals(),允许空键值,可以使用初始容量和负载因子优化HashMap内存使用。散列表冲突处理方式:开放定址法、链表法。HashMap采用链表法
  • TreeMap基于红黑树的数据结构,非线程安全,该树处于平衡状态
  • HashTable线程安全,效率比HashMap低,不允许空键值
  • ConcurrentHashMap作为一种线程安全且高效的哈希表解决方案(分段锁方案),相比HashTable的全表锁性能提高很多
  • HashMap适用于Map中插入、删除、查找元素
  • TreeMap适用于按自然顺序或自定义顺序遍历键值