Java集合总结
1.集合是一个容器,里面可以存放各种元素,就如同去超市购物时用到的购物袋。在集合中存储的都是对象的引用,并不是直接将对象存储再集合中。所有集合都在java.util包下。集合有几个类,不同的集合底层实现的是不同的数据结构。
集合分两类,Collection 和 Map。其继承结构图分别如下图所示:
1.Collection
如上,Collection是一个接口,Collection接口实现了Iterable接口,表示Collection类集合都是可迭代的,可遍历的。Collection集合同时又有多个实现类,其中List和Set是较为常用的,以下初步说明两类集合:
List: List是一个接口, List集合存储元素的的特点是有序可重复的,有序是因为存储的元素都有下标,从0开始,以1递增。同时List也有多个实现类,其中ArrayList,LinkedList,Vector相对比较常用:
ArrayList: ArrayList实现了List接口,他有如下特点:
1.ArrayList底层采用的是数组的数据结构,底层用的是Object[]数组,所以ArrayList对查询的效率很高,而对增删的效率较低,但是向末尾添加元素的时候效率也是很高的。
2.ArrayList集合是非线程安全的。
3.ArrayList集合的默认初始化容量是10(用无参构造创建集合的时候,刚创建的集合容量是0,添加第一个元素的时候容量会变成10),当添加的元素超过集合容量时(如容量10,添加第11个元素时),集合会进行自动扩容,每次扩容是原容量的1.5倍。
4.底层扩容机制其实是数组的复制,将原来的数组复制到另一个内存更大的数组中,并将新元素添加到新的数组中。
5.优化ArrayList:创建的时候给定一个预估的初始化容量,减少他的扩容次数。
LinkedList: LinkedList实现了List接口,他有如下特点:
1.LinkedList底层采用的是双向链表的数据结构,所以其增删的效率很高,但是查询的效率较低。
2.链表中的元素再空间存储上,内存地址不连续。
3.对于LinkedList来说,没有初始化容量,也没有扩容机制,直接再链表中添加即可。
Vector: Vector实现了List接口,这种结合的底层也是采用的数组这种数据结构,但是与ArrayList集合不同的是,他是线程安全的,他的所有方法都有synchronized关键字修饰,但是效率较低,现在保证线程安全有别的方案,所以Vector使用较少了。
Set: Collection的另一个比较重要的分支是Set,Set同样是一个接口,Set存储元素的特点是无序不可重复,Set集合中的元素没有下标。其实存在Set中的元素就是存在了Map集合的key中。Set同样也有多个实现类,HashSet和SortedSet比较常用:
HashSet: 实际上HashSet集合在创建的时候,底层是创建了一个HashMap集合。向HashSet集合中存储元素,实际上是存储到了HaseMap的key中。所以HashSet和HashMap其实是类似的,底层也是哈希表的数据结构,默认初始化容量是16,扩容因子是0.75(当哈希表的数组的容量达到75%的时候开始扩容),扩容之后是原来的2倍。哈希表单向链表中的元素超过8个,单向链表会变成红黑树数据结构,而当红黑树的节点小于6个的时候又会重新变成单向链表。
SortedSet: SortedSet是一个接口,该接口由于实现了Set接口,所以他的特点也是无需不可重复,但是放在Set集合中的元素可以自动排序,称为可排序集合。放到该集合中的元素是自动按大小排序的。在SortedSet下有一个实现类是TreeSet
TreeSet: TreeSet集合底层实际上是TreeMap,创建TreeSet的时候实际上是创建了TreeMap,放在TreeSet中的元素实际是放在了TreeMap的key中。TreeMap底层采用的是二叉树的数据结构,所以可以自动排序。(取数据的时候采用的是中序遍历方式:左根右)
2.Map
Map集合和Collection集合在继承结构上没有任何关系;Map集合是以key-value的这种键值对的方式存储元素的;同样key和value都是存储对象的内存地址的;因为Map集合的key其实就是Set集合,所以所有Map集合key的特点:无序不可重复。Map下较为常用的集合有HashMap、HashTable、SortedMap:
HashMap: HashMap实现了Map接口,底层使用的是哈希表的数据结构,是非线程安全的。默认初始化容量是16,扩容因子是0.75(当哈希表的数组的容量达到75%的时候开始扩容),扩容之后是原来的2倍。哈希表单向链表中的元素超过8个,单向链表会变成红黑树数据结构,而当红黑树的节点小于6个的时候又会重新变成单向链表,这种方式也是为了提高检索效率。HashMap集合的key和value是允许null的。
HashTable: 同HashMap一样,HashTable集合底层也是哈希表的数据结构,但是不同的是HashTable是线程安全的,其中所有的方法都是由synchronized关键字修饰的,但是效率较低,现在基本不用了。HashTable集合的默认初始化容量是11,集合扩容是原容量的2倍+1(原容量*2+1)。HashTable的key和value不允许为空。
Properties: Properties继承于HashTable,所以也是线程安全的。Properties在存储元素的时候key和value只支持String类型,不支持其他类型。Properties被称为属性类。
SortedMap: SortedMap的特点和SortedSet一样,因为放在SortedSet中的元素实际就是放在了SortedMap的key中。其特点首先是无序不可重复,再之是可按照元素的大小自动排序,称为可排序集合。
TreeMap: 继承于SortedMap,底层数据结构是二叉树,与TreeSet相同。
Collection中的公共方法(用ArrayList演示):
1.add(E e):往集合中添加元素。
ArrayList list = new ArrayList();
list.add(1);//往集合中添加元素"1"
2.size():返回int,表示此集合中的元素数。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
System.out.println(list.size());//3
3.clear() :清空集合。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.clear();//清空集合
System.out.println(list.size());//0
4.isEmpty() :判断集中是否为空。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
System.out.println(list.isEmpty());//false
list.clear();//清空集合
System.out.println(list.isEmpty());//true
5.toArray() :返回一个数组,将一个集合转换成一个数组。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
Object[] objects = list.toArray();
for (Object o : objects){
System.out.println(o);//1 2 3
}
6.contains(Object o) : 判断集合中是否包含某元素。该方法底层调用的是equals()方法比较的。如下:
在Dog类没有重写equals()方法时,比较结果是false。
Dog类重写equals()方法之后,返回值是true。
remove(Object o): 从该集合中删除指定元素,同contains()方法,该方法底层也是调用的equals()方法。如下:
在Dog类没有重写equals()方法时,最后结果是1,表示之前的元素并没有被删除。
Dog类重写equals()方法之后,返回值是0,表示已经删除了集合中的元素。
总结:所以需要加入集合中的类最好重写equals()方法。
List特有方法(用ArrayList演示):因为List集合中的元素是有下标的,所以他有自己的一系列特有方法,如下:
1.add(int index, E element) :将指定的元素插入集合中的指定位置。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(1,3);
for (Object o : list){
System.out.println(o);//1 3 2
}
2.get(int index) :返回集合中指定位置的元素。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
System.out.println(list.get(1));//2
3.indexOf(Object o) :对象第一次出现的索引。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(2);
System.out.println(list.indexOf(2));//1
lastIndexOf(Object o) :对象最后一次出现的索引:
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(2);
System.out.println(list.lastIndexOf(2));//2
以上两个方法同样是用equals()方法进行比较的。
4.set(int index, E element) :修改指定位置元素。
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(2);
list.set(2,3);
for (Object o : list){
System.out.println(o);//1 2 3
}
5.remove(int index) :删除指定位置的元素
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.remove(1);
for (Object o : list){
System.out.println(o);//1 3
}
ArrayList的构造方法: ArrayList(Collection<? extends E> c) : ArrayList的构造方法可以传入一个Collection集合,将这个Collection集合转换成一个ArrayList集合。
Set set = new HashSet();
set.add(1);
set.add(3);
set.add(4);
ArrayList list = new ArrayList(set);
Map的公共方法(用HashMap演示):
1.clear() :清空集合。
2.put(K key, V value) :向map中添加元素,以key-value的方式,如同Collection中的add方法。
3.isEmpty():判断集合是否为空。
4.size():返回集合的元素个数。
5.get(Object key) :通过key获取value。
Map map = new HashMap();
map.put(1,"张三");
map.put(2,"李四");
System.out.println(map.get(2));//李四
6.containsKey(Object key) :判断集合中是否包含某个key。
containsValue(Object value) :判断集合中是否包含某个value。
remove(Object key) :删除集合中对应的key的数据。
以上三个方法底层都是调用的equals()方法进行比较,所以最好放进集合中的类都重写equals()方法。
7.keySet() :将Map中的key全部拿出来放进一个Set集合中,返回一个Set集合。其实Map的key就是一个Set集合。
Map map = new HashMap();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"李四");
Set set = map.keySet();
for (Object o : set){
System.out.println(o);//1 2 3
}
8.values() :将Map集合中的values全部拿出来放进一个Collection中,返回一个Collection集合。
Map map = new HashMap();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"李四");
Collection list = map.values();
for (Object o : list){
System.out.println(o);//张三 李四 李四
}
9.entrySet() :将Map转换为Set。该方法的返回值类型是Set<Map.Entry<K,V>>,这是一种类型名,是Map中的静态内部类。
Map map = new HashMap();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"李四");
Set set = map.entrySet();
for (Object o : set){
System.out.println(o);//1=张三 2=李四 3=李四
}
Collection的遍历:
第一种方法:因为Collection实现了Iterable接口,所以所有的集合都是可以使用迭代器遍历的,调用iterator()方法可以获得迭代器,在用循环就能完成遍历,如下:
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
Iterator it = list.iterator();
while (it.hasNext()){
Object o = it.next();
System.out.println(o);// 1 2 3
}
Iterator有三个方法,next()、hasNext()、remove()。
hasNext()方法判断集合中是否含有下一个元素,
next()让迭代器返回下一个元素,
remove() 方法则是删除迭代器返回的最后一个元素,也就是删除迭代器当前所指定的元素。
但是值得注意的是,集合的结构发生改变,迭代器必须重新获取,否则会报异常
这里用list的remove()方法改变了集合的结构,所以会报异常。但是如果使用迭代器的remove()方法则不会改变集合的结构,迭代器类似一个快照。
结论:所以,一代改变集合的结构,则需要重新获取迭代器。
第二种方法: 使用增强for循环遍历,代码简单,但是效率没有迭代器高。
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
for (Object o :list){
System.out.println(o);//1 2 3
}
Map集合的遍历:
第一种方法: 获取所有的key,通过key获取value。
第二种方法: 用entrySet()方法,直接将Map集合转为set集合,遍历set集合,每次取出一个node对象,在调用node.getkey(),node.getValue(),获取key和value。 这种效率比第一种方法高,适合数据量大的时候使用。
从TreeMap的put方法的源代码上我们可以看到这里调用了compareTo()方法,这个compareTo()方法来自一个Comparable接口。
所以我们可以了解到,需要SortedMap集合对元素进行自动排序,必须将放入SortedMap中的元素类实现Comparable,并且实现这个接口中的compareTo()方法,在这个方法中编写比较规则,集合才能自动排序。
如上,要是Dog类没有实现Comparable接口,调用add()方法则直接报异常。
Dog类实现了Comparable接口,并重写了CompareTo()方法之后,就可以完成自动排序了。
以上方法是第一种可以让SortedMap和SortedSet实现自动排序的功能,还有第二种方法,使用比较器:
比较器需要实现java.util.Comparator接口,并重写compare()方法。在构建TreeMap集合的时候使用构造方法将比较器传入,就可以实现元素的自动排序功能,如下图:
最终结论: 放在TreeSet或者TreeMap中的元素,想要排序,有两种方式
1.实现Comparable接口。
2.创建比较器传入。
直接上老杜的图:
注:上图中的注意中说同一个链表上所有的hash值相同,其则不然,有可能是不同的,当时笔记没有改过来。
结论:放在HashMap集合key部分的以及放在HashSet集合中的元素,需要同时重写hashCode()方法和equals()方法。
java.util.Collections类中有几个方法:
1.Collections.synchronizedList(List list)方法,可以将非线程安全的集合变成线程安全的。
List list = new ArrayList();
List list2 = Collections.synchronizedList(list);
2.sort(List list)方法,对集合进行排序。跟TreeSet相同,需要集合中的元素实现comparable接口,或者使用sort(List list,Comparator c)方法传入比较器。
运用该方法,也可以对Set集合进行排序,首先用ArrayList的构造方法将Set集合转为ArrayList集合,在使用sort(List list)方法进行排序。
以上便是我对集合章节的总结。