Java容器的理解
容器,顾名思义,它是用来装载其他东西的。
在Java中,定义了一个Collection接口,定义了三个子接口,分别是Set,Map,List,并将它们实现为类;
下面就分别来介绍这三种常见容器,首先是Set ,Set 作为一个集合的概念来理解,它里面的内容没有固定顺序,不可以重复,常见的Set有HashSet;
接下来是Map ,定义了key-value,键值对的存储方法;
Java中数据存储方式最底层的两种结构,一种是数组,另一种就是链表,数组的特点:连续空间,寻址迅速,但是在删除或者添加元素的时候需要有较大幅度的移动,所以查询速度快,增删较慢。而链表正好相反,由于空间不连续,寻址困难,增删元素只需修改指针,所以查询慢、增删快。
哈希表。哈希表具有较快(常量级)的查询速度,及相对较快的增删速度,所以很适合在海量数据的环境中使用。一般实现哈希表的方法采用“拉链法”,我们可以理解为“链表的数组”
常见的hashMap和hashTable,
HashMap的存储规则:
哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。如果出现Hash冲突, 将在数组的该位置拉伸出链表进行存储(在链表的尾部进行添加), 如果链表的长度大于设定值后, 将链表转为红黑树.
下面来看一下它常见的
源码一
1 public HashMap() { 2 this.loadFactor = DEFAULT_LOAD_FACTOR; //装载因子 3 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); 4 table = new Entry[DEFAULT_INITIAL_CAPACITY]; //起始容量 5 init(); 6 }
源码二
1 public HashMap(int initialCapacity, float loadFactor) { 2 if (initialCapacity < 0) //判断是否合法 3 throw new IllegalArgumentException("Illegal initial capacity: " + 4 initialCapacity); 5 if (initialCapacity > MAXIMUM_CAPACITY) //判断是否超过默认最大值,如果超过,改为最大值 6 initialCapacity = MAXIMUM_CAPACITY; 7 if (loadFactor <= 0 || Float.isNaN(loadFactor)) //装载因子 8 throw new IllegalArgumentException("Illegal load factor: " + 9 loadFactor); 10 11 // Find a power of 2 >= initialCapacity 12 int capacity = 1; 13 while (capacity < initialCapacity) 14 capacity <<= 1; 15 16 this.loadFactor = loadFactor; 17 threshold = (int)(capacity * loadFactor); 18 table = new Entry[capacity]; 19 init(); 20 }
HashTable的存储规则:
优先使用数组存储, 存储元素时, 先取出下标上的元素(可能为null), 然后添加到数组元素Entry对象的next属性中(在链表的头部进行添加).
出现Hash冲突时, 新元素next属性会指向冲突的元素. 如果没有Hash冲突, 则新元素的next属性就是null
1、HashMap是非线程安全的,HashTable是线程安全的。
2、HashMap的键和值都允许有null值存在,而HashTable则不行。
HashMap |
HashTable |
key, value 均可以为 null |
key, value 均不可以为 null |
最后是List,它是作为链表的概念来理解,里面的元素有固定顺序,也可以重复;
它们的父接口Collection定义了一些常见的方法,比如
int size();// 返回容器的长度
boolean isEmpty();//判断是否为空
boolean contains(Object element);//判断是否包含某一个元素
void clear(); //清空
boolean add();//添加
boolean remove();移除
Iterator接口,这是额外值得注意的东西,由于每个容器都各不相同,很难有一个统一的方法去遍历容器内的元素,因此每个容器都实现了Iterator方法,返回一个Iterator对象,可以对本身进行控制,只有容器自己,最了解容器的操作,包括遍历,删除,添加。
iterator常见的函数有:::
boolean hasNext(); //判断是否存在下一个
Object next(); //返回下一个元素
void remove(); //删除,注意,Iterator对象的remove方法是Collection在迭代过程中,删除元素唯一安全的方法。
Iterator方法的思想中也有父类引用指向子类对象的思想,Java封装性的体现。
Object[] toArray() 将Object类的链表转换为Object类的数组
容器里常用的Java概念就是父类引用指向子类对象,经常需要重写的方法有equals()方法,hascode()方法,实现了Compare able接口的类重写compareTo()方法,
toString()方法,线程里的run方法。
泛型的由来,存入集合的元素都被当作Object类来对待,导致各个数据类型失去了自己的特性,解决的方法就是在定义容器对象的过程中,直接定义容器内元素的类型,这样程序的稳定性和可读性就好很多。