Go语言入门3 – 容器

容器

  • Array 数组
  • Slice 切片(可以看成动态的数组)
  • Map 映射

一、Array 数组

  • [10]int 和 [20]int 是不同类型,数组类型相同要长度和元素类型完全相同才可以
  • func loopArray(arr2 [3]int) 是值传递,入参会拷贝一份数组,所以如果数组很大,从内存和性能上函数传递数组值都是很大的开销,需要避免(使用指针可以实现”引用传递” func loopArray(arr2 *[3]int),调用方传入 &arr2)
  • 在 Go 中一般不直接使用数组,而是使用切片
  • 数组是定长的,不可扩展,切片相当于动态数组
import "fmt"

func defineArray() [3]int {
   // 定义数组,不赋初值(使用默认值)
   var arr1 [5]int // [0 0 0 0 0]
   // 定义数组,赋初值
   arr2 := [3]int{1, 2, 3} // [1 2 3]
   // 定义数组,由编译器来计算长度,不可写成[],不带长度或者 ... 的表示切片
   arr3 := [...]int{4, 5, 6, 7} // [4 5 6 7]
   // 创建指针数组
   arr4 := [2]*string{new(string), new(string)}
   *arr4[0] = "hello"
   *arr4[1] = "go"
   // 为指定索引位置设置值
   arr5 := [3]int{1:10} // [0,10,0]
   // 二维数组
   var grid [4][5]int // [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
   // 数组拷贝,直接复制一份 arr2 给 arr6
   arr6 := arr2

   fmt.Println(arr1, arr2, arr3, arr4, arr5, arr6, grid)// arr4 打印出来的是地址 [0xc00000e1e0 0xc00000e1f0]
   fmt.Println(*arr4[0]) // hello
   return arr2
}

// 数组是值传递,这里的入参会拷贝一份数组(使用指针可以实现"引用传递")
func loopArray(arr2 [3]int) {
   // 通用方法
   for i := 0; i < len(arr2); i++ {
      fmt.Println(arr2[i])
   }
   // 最简方法,只获取数组下标
   for i := range arr2 {
      fmt.Println(arr2[i])
   }
   // 最简方法,获取数组下标和对应的值
   for i, v := range arr2 {
      fmt.Println(i, v)
   }
   // 最简方法,只获取值,使用 _ 省略变量
   for _, v := range arr2 {
      fmt.Println(v)
   }
}

二、Slice 切片

切片是围绕动态数组的概念构建的,可以按需自动增长和缩小。切片的动态增长是通过内置函数 append 来实现的。这个函数可以快速且高效地增长切片。还可以通过对切片再次切片来缩小一个切片的大小。

切片有 3 个字段分别是指向底层数组的指针、切片访问的元素的个数(即长度)和切片允许增长到的元素个数(即容量)

5842684-3ed9eb450701d434

从切片 slice1 创建出来的切片 slice2,slice1 和 slice2 共享底层数组,一个修改了共享部分的元素,另一个也会感知

2.1、创建切片

// 1、使用make函数创建一个字符串切片,长度和容量都是5
slice1 := make([]string, 5)

// 2、创建一个int切片,长度是3,容量是5
slice2 := make([]int, 3, 5)

// 3、使用字面量创建切片,长度是3,容量是3
slice3 := []int{1, 2, 3} // [3]int{1, 2, 3}

// 4、创建 nil 切片,长度为0,容量为0
var slice4 []int

// 5、创建空切片,长度为0,容量为0
slice5 := make([]int, 0)
slice6 := []int{}

// 6、自定义底层数组,通过该底层数组创建切片
arr := [5]int{1, 2, 3, 4, 5}

// 数组转化为切片,左闭右开 [arr[2]~arr[4])
slice7 := arr[2:4] // [3,4]
slice8 := arr[2:]  // [3,4,5]
slice9 := arr[:4]  // [1,2,3,4]
slice10 := arr[:]   // [1,2,3,4,5]

实际上还有第七种创建切片的方式:根据切片创建切片,称为 reslice

2.2、切片使用

slice1 := []int{1, 2, 3, 4, 5}

// 1、根据索引获取切片元素
fmt.Println(slice1[1]) // 2

// 2、根据索引修改切片元素
slice1[3] = 400
fmt.Println(slice1) // [1, 2, 3, 400, 5]

// 3、根据切片创建切片,和根据自定义数组创建切片方式相同,长度是2=3-1,容量是4=5-1
// 但是需要格外注意,新生成的切片 slice2 和原始切片 slice1 的指针元素指向了相同的底层数组,所以修改元素要注意
slice2 := slice1[1:3] // [2, 3]
slice2[1] = 300
fmt.Println(slice2) // [2, 300]
fmt.Println(slice1) // [1, 2, 300, 400, 5] slice1也发生了变化

// 4、拷贝 slice 中的元素
fmt.Println("copy")
slice3 := []int{0, 0, 0, 0, 0}
slice4 := []int{1, 2, 3}
copy(slice3, slice4)
fmt.Println(slice3) // [1, 2, 3, 0, 0]
fmt.Println(slice4) // [1, 2, 3]    

// 5、删除 slice 中的元素,删除slice5[2]=3
fmt.Println("delete")
slice5 := []int{1, 2, 3, 4}
slice5 = append(slice5[:2], slice5[3:]...)
fmt.Println(slice5) // [1, 2, 4]

2.3、append 增加切片长度

5842684-081821049d9e13f3

// 1、创建原始切片,长度是5,容量是5
slice := []int{10, 20, 30, 40, 50}

// 2、reslice 新切片,长度是2,容量是4
newSlice := slice[1:3] // [20, 30]

// 由于底层数组还有容量,可以直接追加元素而容量不变
newSlice = append(newSlice, 60) // [20, 30 ,60] 长度是3,容量是4
fmt.Println(newSlice)           // [20, 30 ,60]
fmt.Println(slice)              // [10, 20, 30 ,60, 50]

// 长度4,容量4
slice := []int{10, 20, 30, 40}
// 此时切片容量用完了,再追加需要扩容,此处会新加数组,长度为原数组的2倍,即 newSlice 的底层数组是新数组,新切片容量为8;
// 而 slice 的底层数组是旧数组,二者互不影响
newSlice := append(slice, 50)
fmt.Println(slice)    // [10, 20, 30, 40]
fmt.Println(newSlice) // [10, 20, 30, 40, 50]
newSlice[0] = 100
fmt.Println(slice)    // [10, 20, 30, 40]
fmt.Println(newSlice) // [100, 20, 30, 40, 50]

这里是我自己的测试代码:

b := make([]string, 3, 5)
b[0] = "A"
b[1] = "B"
b[2] = "C"
//b[3]="C1"
//b[4]="C2"
b_1 := append(b, "D")
b_1 = append(b, "E")
fmt.Println("b:", b)
fmt.Println("b_1:", b_1)

b1 := []int{1, 2, 3, 4, 5}
var b2 []int
b3 := b1[0:2]
fmt.Println(b1)

b1[0] = 100

fmt.Println(b2)
fmt.Println(b3)
fmt.Println(&b1[0])
fmt.Println(&b3[0])
fmt.Println(len(b3))

fmt.Println("b1:", b1)
b1_1 := append(b1, 6);
b1_2 := append(b1_1, 7);
b1_3 := append(b1_2, 8);
b1_3 = append(b1_3, 9);
b1_3 = append(b1_3, 10);
b1_3 = append(b1_3, 11);
b1_3 = append(b1_3, 12);
b1_3 = append(b1_3, 13);
b1_3 = append(b1_3, 14);
b1_3 = append(b1_3, 15);
b1_3 = append(b1_3, 16);
b1_3 = append(b1_3, 17);
b1_3 = append(b1_3, 18);
b1_3 = append(b1_3, 19);
b1_3 = append(b1_3, 20);
b1_3 = append(b1_3, 21);
//b1_1 = append(b1, 9);
//b1_1 = append(b1, 10);

fmt.Println("b1_1:", b1_1)
fmt.Println("b1_2:", b1_2)
fmt.Println("b1_3:", b1_3)
fmt.Println("b1_3:", len(b1_3))
fmt.Println("b1_3:", cap(b1_3))

5842684-dc4d519b7d344b50

  • 当切片容量(而非数组长度,默认切片容量等于数组长度,也可以显示指定)用完了,再追加需要扩容,此处会新建数组,长度为原数组的2倍,然后将旧数组元素拷贝到新数组,newSlice 的底层数组是新数组,newSlice 容量为8;而 slice 的底层数组是旧数组,二者互不影响。
  • slice 扩容机制:在切片的容量小于 1000 个元素时,总是会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次增加 25% 的容量。

2.4、显示设置容量

在没有显示指定容量的情况下,切片容量就是其底层数组的长度,如果在创建切片时设置切片的容量和长度一样,就可以强制让新切片的第一个 append 操作创建新的底层数组,与原有的底层数组分离。新切片与原有的底层数组分离后,可以安全地进行后续修改

source := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
// 长度为1=3-2,容量为1=3-2  source[i:j:k] 长度=j-i 容量=k-i
slice := source[2:3:3]
fmt.Println(source) // ["Apple", "Orange", "Plum", "Banana", "Grape"]
fmt.Println(slice) // ["Plum"]
// 超出切片容量3,需要新建数组
slice = append(slice, "Kiwi")
fmt.Println(source) // ["Apple", "Orange", "Plum", "Banana", "Grape"]
fmt.Println(slice) // ["Plum", "Kiwi"]

2.5、合并切片

s1 := []int{1, 2}
s2 := []int{3, 4}
fmt.Println(append(s1,s2...)) // [1, 2, 3, 4]

2.6、迭代切片

  slice := []int{10, 20, 30, 40}
    // 与数组迭代一样,可以使用 for range + 普通 for 循环
    for index,value := range slice {
        fmt.Println(index, value)
    }

2.7、函数间传递切片

在函数间传递切片就是要在函数间以值的方式传递切片。由于切片的尺寸很小,在函数间复制和传递切片成本也很低;而在函数间传递数组是需要拷贝整个数组的,所以内存和性能上都不好
调用函数,传递一个切片副本,实际上内部还是传递了对数组的指针,所以 foo 内部的操作会影响 main 中的 slice。

import "fmt"

func foo(slice []int) []int {
   slice[0] = 100
   return slice
}

func main() {
   // 1、创建一个 slice
   slice := []int{1, 2, 3, 4, 5}
   fmt.Println(slice) // [1, 2, 3, 4, 5]
   // 2、调用函数,传递一个切片副本,实际上内部还是传递了对数组的指针,
   // 所以 foo 内部的操作会影响 main 中的 slice
   slice2 := foo(slice)
   fmt.Println(slice2) // [100, 2, 3, 4, 5]
   fmt.Println(slice) // [100, 2, 3, 4, 5]
}

三、Map 映射

  • Map 是一个存储键值对的无序集合,就是说不保证顺序
  • Slice、Map、function 以及包含切片的结构类型不能作为 Map 的 key
  • map在函数间传递,不会拷贝一份map,相当于是”引用传递”,所以remove函数对传入的map的操作是会影响到main函数中的map的

3.1、基本用法

// 1、使用 make 创建 map,key为string,value为int
map1 := make(map[string]int)
// 2、使用字面量创建 map - 最常用的姿势,key为string,value为slice,初始值中的slice可以不加 []string 定义
map2 := map[string][]string{"hi": {"go", "c"}, "hello": []string{"java"}}
// 3、创建空映射
map3 := map[string]string{} // map3 := map[string]string nil映射
fmt.Println(map1, map2, map3)

// 4、向映射添加值
fmt.Println("map put")
map3["a"] = "x"
map3["b"] = "y"
fmt.Println(map3) // map[a:x b:y]

// 5、获取值并判断是否存在
value, exist := map3["c"]
if exist {
fmt.Println(value)
} else {
fmt.Println("map3[\"c\"] does not exist")
}

// 6、迭代
fmt.Println("iterat")
for key, value := range map3 {
fmt.Println(key, value)
}

// 7、从 map 中删除元素
delete(map3, "a")
fmt.Println(map3) // map[b:y]

以下是我的笔记代码:

m1 := make(map[string]string)
m1["a"] = "A"
m1["b"] = "B"
m1["c"] = "C"
m1["d"] = "D"
fmt.Println(m1)

m2 := map[int]int{}
m2[0] = 100
m2[1] = 100 * m2[0]
m2[2] = 100 * m2[0]
fmt.Println(m2)

parserS1(m1);
//delete(m1,"a")
v, m_1 := m1["a"]
if m_1 {
   fmt.Println("存在的", v)
} else {
   fmt.Println("不存在的", m_1)
}

for i, n := range m1 {
   fmt.Printf("me-> Key %s : %s \n", i, n)
}

3.2、函数间传递映射

import "fmt"

// map在函数间传递,不会拷贝一份map,相当于是"引用传递",所以remove函数对传入的map的操作是会影响到main函数中的map的
func remove(map4 map[int]int)  {
   delete(map4, 1)
}

func main() {
   map4 := map[int]int{0:0, 1:1, 2:2}
   fmt.Println(map4) // map[0:0 1:1 2:2]
   remove(map4)
   fmt.Println(map4) // map[0:0 2:2]
}

 

Java集合类(Set、List、Map)及排序

Java集合类如下图:

  • set是一个数据集合
  • List属于数组列表
  • Map也属于一个数据集合

866452-20170226172935866-1277792347

网上找了个图,方便理解。

  • List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口
  • Set下有HashSet,LinkedHashSet,TreeSet
  • List下有ArrayList,Vector,LinkedList
  • Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
  • Collection接口下还有个Queue接口,有PriorityQueue类

1.Set对象

Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。

Modifier and Type Method and Description
boolean add(E e)

Adds the specified element to this set if it is not already present (optional operation).
boolean addAll(Collection<? extends E> c)

Adds all of the elements in the specified collection to this set if they’re not already present (optional operation).
void clear()

Removes all of the elements from this set (optional operation).
boolean contains(Object o)

Returns true if this set contains the specified element.
boolean containsAll(Collection<?> c)

Returns true if this set contains all of the elements of the specified collection.
boolean equals(Object o)

Compares the specified object with this set for equality.
int hashCode()

Returns the hash code value for this set.
boolean isEmpty()

Returns true if this set contains no elements.
Iterator<E> iterator()

Returns an iterator over the elements in this set.
boolean remove(Object o)

Removes the specified element from this set if it is present (optional operation).
boolean removeAll(Collection<?> c)

Removes from this set all of its elements that are contained in the specified collection (optional operation).
boolean retainAll(Collection<?> c)

Retains only the elements in this set that are contained in the specified collection (optional operation).
int size()

Returns the number of elements in this set (its cardinality).
default Spliterator<E> spliterator()

Creates a Spliterator over the elements in this set.
Object[] toArray()

Returns an array containing all of the elements in this set.
<T> T[] toArray(T[] a)

Returns an array containing all of the elements in this set; the runtime type of the returned array is that of the specified array.

 

HashSet

HashSet底层用的是哈希表,它把对象根据其哈希值存放到对应的区域里。由于这种特性,两个在不同区域的对象会被认为不相同的。
所以如果对象要存放到Hash集合里面,则需要重写对象的hashCode方法,让相等的对象的hashCode的值也相等。

TreeSet

TreeSet采用的数据结构是红黑树,我们可以让它按指定规则对其中的元素进行排序。它又是如何判断两个元素是否相同呢?除了用equals方法检查两个元素是否相同外,还要检查compareTo方法是否返回为0。

所以如果对象要存放到Tree集合里,需要在重写compareTo方法,把相同的对象的比较值定为0,防止相同的元素被重复添加进集合中。

LinkedHashSet

底层数据结构是链表和哈希表。(FIFO插入有序,唯一)

  • 由链表保证元素有序
  • 由哈希表保证元素唯一

set的测试:

Set<String> data = new TreeSet<>();
data.add("re");
data.add("idn");
data.add("mob");
data.add("ref");
data.add("01");
out(data);

Set<Integer> integers = new TreeSet<>();
integers.add(12);
integers.add(1);
integers.add(15);
integers.add(5);
integers.add(65);

out(integers);

 

打印结果如下:

[01, idn, mob, re, ref]
[1, 5, 12, 15, 65]

2.List对象

List 接口继承了 Collection 接口以定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

Modifier and Type Method and Description
boolean add(E e)

Appends the specified element to the end of this list (optional operation).
void add(int index, E element)

Inserts the specified element at the specified position in this list (optional operation).
boolean addAll(Collection<? extends E> c)

Appends all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection’s iterator (optional operation).
boolean addAll(int index, Collection<? extends E> c)

Inserts all of the elements in the specified collection into this list at the specified position (optional operation).
void clear()

Removes all of the elements from this list (optional operation).
boolean contains(Object o)

Returns true if this list contains the specified element.
boolean containsAll(Collection<?> c)

Returns true if this list contains all of the elements of the specified collection.
boolean equals(Object o)

Compares the specified object with this list for equality.
E get(int index)

Returns the element at the specified position in this list.
int hashCode()

Returns the hash code value for this list.
int indexOf(Object o)

Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
boolean isEmpty()

Returns true if this list contains no elements.
Iterator<E> iterator()

Returns an iterator over the elements in this list in proper sequence.
int lastIndexOf(Object o)

Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
ListIterator<E> listIterator()

Returns a list iterator over the elements in this list (in proper sequence).
ListIterator<E> listIterator(int index)

Returns a list iterator over the elements in this list (in proper sequence), starting at the specified position in the list.
E remove(int index)

Removes the element at the specified position in this list (optional operation).
boolean remove(Object o)

Removes the first occurrence of the specified element from this list, if it is present (optional operation).
boolean removeAll(Collection<?> c)

Removes from this list all of its elements that are contained in the specified collection (optional operation).
default void replaceAll(UnaryOperator<E> operator)

Replaces each element of this list with the result of applying the operator to that element.
boolean retainAll(Collection<?> c)

Retains only the elements in this list that are contained in the specified collection (optional operation).
E set(int index, E element)

Replaces the element at the specified position in this list with the specified element (optional operation).
int size()

Returns the number of elements in this list.
default void sort(Comparator<? super E> c)

Sorts this list according to the order induced by the specified Comparator.
default Spliterator<E> spliterator()

Creates a Spliterator over the elements in this list.
List<E> subList(int fromIndex, int toIndex)

Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.
Object[] toArray()

Returns an array containing all of the elements in this list in proper sequence (from first to last element).
<T> T[] toArray(T[] a)

Returns an array containing all of the elements in this list in proper sequence (from first to last element); the runtime type of the returned array is that of the specified array.

ArrayList

  • 优点: 底层数据结构是数组,查询快,增删慢。
  • 缺点: 线程不安全,效率高

Vector

  • 优点: 底层数据结构是数组,查询快,增删慢。
  • 缺点: 线程安全,效率低

LinkedList

  • 优点: 底层数据结构是链表,查询慢,增删快。
  • 缺点: 线程不安全,效率高

PS:ArrayList是实现了基于底层的数据结构,LinkedList基于底层数据结构是链表;对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList遍历全部再确定;对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

Collection是集合接口

|————Set子接口:无序,不允许重复。
|————List子接口:有序,可以有重复元素。

区别:Collections是集合类

Set和List对比:

  • Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
  • List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

 

List的排序示例:

List<Integer> list = new ArrayList<>();
list.add(12);
list.add(1);
list.add(15);
list.add(5);
list.add(65);
list.sort((a,b)->{
    return a-b;
});
out(list);

打印结果:
[1, 5, 12, 15, 65]

如果降序,只需要把a-b改成b-a即可。

 

3.Map对象

Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。

map主要的特性:

  • key字段不可重复
  • key,value 都可以是任何引用类型的数据,包括 null
  • Map 取代了古老的 Dictionary 抽象类

因此,每个 key 只能对应一个 value, 多个 key 可以对应一个 value。Map集合维护“键、值对”的关联性,使你可以通过“键”查找“值”。

Modifier and Type Method and Description
void clear()

Removes all of the mappings from this map (optional operation).
default V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping).
default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)

If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.
default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
boolean containsKey(Object key)

Returns true if this map contains a mapping for the specified key.
boolean containsValue(Object value)

Returns true if this map maps one or more keys to the specified value.
Set<Map.Entry<K,V>> entrySet()

Returns a Set view of the mappings contained in this map.
boolean equals(Object o)

Compares the specified object with this map for equality.
default void forEach(BiConsumer<? super K,? super V> action)

Performs the given action for each entry in this map until all entries have been processed or the action throws an exception.
V get(Object key)

Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
default V getOrDefault(Object key, V defaultValue)

Returns the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key.
int hashCode()

Returns the hash code value for this map.
boolean isEmpty()

Returns true if this map contains no key-value mappings.
Set<K> keySet()

Returns a Set view of the keys contained in this map.
default V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value.
V put(K key, V value)

Associates the specified value with the specified key in this map (optional operation).
void putAll(Map<? extends K,? extends V> m)

Copies all of the mappings from the specified map to this map (optional operation).
default V putIfAbsent(K key, V value)

If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.
V remove(Object key)

Removes the mapping for a key from this map if it is present (optional operation).
default boolean remove(Object key, Object value)

Removes the entry for the specified key only if it is currently mapped to the specified value.
default V replace(K key, V value)

Replaces the entry for the specified key only if it is currently mapped to some value.
default boolean replace(K key, V oldValue, V newValue)

Replaces the entry for the specified key only if currently mapped to the specified value.
default void replaceAll(BiFunction<? super K,? super V,? extends V> function)

Replaces each entry’s value with the result of invoking the given function on that entry until all entries have been processed or the function throws an exception.
int size()

Returns the number of key-value mappings in this map.
Collection<V> values()

Returns a Collection view of the values contained in this map.

Map的功能方法

  • put(Object key, Object value)添加一个“值”(想要得东西)和与“值”相关联的“键”(key)(使用它来查找)。
  • get(Object key)返回与给定“键”相关联的“值”。
  • containsKey()和containsValue()可以检查Map中是否包含某个“键”或“值”。

标准的Java类库中包含了几种不同的Map:

HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap。

它们都有同样的基本接口Map,但是行为、效率、排序策略、保存对象的生命周期和判定“键”等价的策略等各不相同。

执行效率是Map的一个大问题,看看get方法的代码,就会明白为什么在ArrayList中搜索“键”是相当慢的。而这正是HashMap提高速度的地方。HashMap使用了特殊的值,称为“散列码”(hash code),来取代对键的缓慢搜索。“散列码”是“相对唯一”用以代表对象的int值,它是通过将该对象的某些信息进行转换而生成的。所有Java对象都能产生散列码,因为hashCode()是定义在基类Object中的方法。

HashMap

Map基于散列表的实现,就是使用对象的hashCode()进行快速查询的,插入和查询“键值对”的开销是固定的,此方法能够显着提高性能。

 

LinkedHashMap

类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU算法)的次序;只比HashMap慢一点,而在迭代访问时发而更快,因为它使用链表维护内部次序。

TreeMap

基于红黑树数据结构的实现,查看“键”或“键值对”时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特点在于,你得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。

WeakHashMao

弱键(weak key)Map,Map中使用的对象也被允许释放: 这是为解决特殊问题设计的。如果没有map之外的引用指向某个“键”,则此“键”可以被垃圾收集器回收。

IdentifyHashMap

使用==代替equals()对“键”作比较的hash map,专为解决特殊问题而设计。

总结

List,Set,Map将持有对象一律视为Object型别;Collection、List、Set、Map都是接口,不能实例化。继承自它们的 ArrayList, Vector, HashTable, HashMap是具体的对象,可被实例化。vector容器确切知道它所持有的对象隶属什么型别,vector不进行边界检查。

注意:

  • 如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  • 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
  • 在除需要排序时使用TreeSet、TreeMap外,其他应使用HashSet,HashMap,因为他们的效率更高。
  • 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  • 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置,一旦将对象置入容器内,便损失了该对象的型别信息。
  • 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
  • Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。
  • Set和Collection拥有一模一样的接口。
  • List,可以通过get()方法来一次取出一个元素。
  • 一般使用ArrayList;用LinkedList构造堆栈stack、队列使用queue。
  • Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key、alue。
  • HashMap会利用对象的hashCode来快速找到key。
  • Map中元素,可以将key序列、value序列单独抽取出来。
  • 使用keySet()抽取key序列,将map中的所有keys生成一个Set。
  • 使用values()抽取value序列,将map中的所有values生成一个Collection。
  • 为什么一个生成Set,一个生成Collection呢?因为,key总是独一无二的,value允许重复。

 

 
Copyright © 2008-2021 lanxinbase.com Rights Reserved. | 粤ICP备14086738号-3 |