集合之间的关系如下: 实线边界是实现类,折线边界是抽象类,虚线边界是接口(
collection:(Java.util.*; JDK 1.2 )
有以下核心方法。
在Collection接口下,有两个重要的子接口列表和集。
(List )允许元素重复。) JDK1.2 )允许添加空值,允许添加多个空值。
对于Collation,添加了以下两种常用方法:
get )--从索引中获取元素
set )--修改指定索引元素的内部,并返回修改前的内容
ArrayList(JDK1.2 )的基础是数组,线程同步不安全。
本质是对象的动态排列。
默认容量为10,当数据超过当前容量时会自动扩展。 扩展规则如下:
新容量=old容量(old容量1;
也就是说,按原始数组的1/2进行放大。
简单示例:
import java.util.ArrayList; import java.util.List; publicclasstestcollection { publicstaticvoidmain (字符串[ ] args ) {列表集成器列表=new ArrayList ); 建议在创建ArrayList对象时通常上载到List,在//定义对象时添加通用list.add(9)。 list.add(5; list.add(2; list.set (2,7 ); //List以自己的方式将下标为2的元素修改为7.system.out.println (list.get (1) )。 }链接列表(JDK 1.2 )的底层是双向链表,线程同步不安全。
简单示例:
import java.util.LinkedList; import java.util.List; publicclasstestcollection { publicstaticvoidmain [ ] args } { listintegerlist=new linked list (} ); 建议在创建链接的列表对象时通常上变频到列表,在//定义时使用通用list.add(9)。 list.add(5; list.add(2; list.set (2,7 ); //List以自己的方式将下标为2的元素修改为7.system.out.println (list.get (1) )。 }Vector(JDK1.0 )的下层是数组,线程同步是安全的。 但是,效率低下,一般很少使用。
简单示例:
import java.util.List; import java.util.Vector; publicclasstestcollection { publicstaticvoidmain (字符串[ ] args ) {列表integer list=new vector ); 建议在创建Vector对象时通常将其上载到List,在//定义时添加通用list.add(9)。 list.add(5; list.add(2; list.set (2,7 ); //List以自己的方式将下标为2的元素修改为7.system.out.println (list.get (1) )。 }
可以看到,ArrayList , LinkedList , Vector 在使用上的差别并不大。因此我们根据自己的需要选择合适的存储结构,比如插入删除比较多的时候就选用LinkedList , 如果随机访问比较多就使用ArrayList , 如果要求线程安全就选用Vector 。
ArrayList和Vector的区别:
1 .载体从JDK1.0开始,ArrayList从JDK1.2开始
2 .加载方法: Vercor的基础基于数组,在实例化时直接创建数组。 ArrayList是一种懒惰的加载机制,在第一次添加数据时创建数组。
3 .扩展方式: Vector默认容量为10,扩展机制为newCapacity=2*oldCapacity,ArrayList默认容量为10,扩展机制为new capacity=oldcapacityoldcapacity
4 .输出方式: Vector多支持枚举输出方式。
3.Vector线程安全,ArrrayList线程不安全。
4.Vector同步处理的性能低,ArrayList异步处理的性能高。
ArrayList和LinkedList的区别:
沥
nkedList和ArrayList的差别主要来自于Array和LinkedList数据结构的不同。1) 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
2) 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
3) 类似于插入数据,删除数据时,LinkedList也优于ArrayList。
4) LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
如果List需要存储引用类型,并且使用到 .remove() , contains() 等方法,建议复写该引用类型的 .equals() 方法。
以下为复写 .equals() 的完整Student 实例
import java.util.ArrayList;import java.util.List;class Student {public int id;public String name;public Student(int id, String name) {this.id = id;this.name = name;}@Overridepublic String toString() {return "id : " + id + " name : " + name;}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;//如果传进来的对象就是当前对象就直接返回true}if (obj instanceof Student) {Student stu = (Student) obj;if (this.id == stu.id) {if (this.name.equals(stu.name)) {return true;}return false;}return false;}return false;}}public class TestCollection {public static void main(String[] args) {List<Student> stu = new ArrayList<>();stu.add(new Student(1, "andy"));stu.add(new Student(2, "john"));stu.add(new Student(3, "jack"));Student black = new Student(4, "black");stu.add(black);System.out.println("stu是否包含这个对象:"+stu.contains(new Student(3, "jack")));stu.remove(new Student(1, "andy"));System.out.println(stu.get(0));//id=1 的 andy 被成功删除}}打印结果:
stu是否包含这个对象:true
id : 2 name : john //原来的第一个Student对象andy被成功删除
那出现这个现象的原因是什么呢?
点击 此处 查看详细原因。
Set:不允许元素重复.(JDK1.2) ---- 本质为没有value的Map HashSet(JDK1.2)
底层为数组(Hash表),实际上就是HashMap。
允许为null,不能重复,元素乱序存储。
Set不允许元素重复,那么在存储这个元素时会判断这个元素是否重复,因为他的底层就是用了一个HashMap来实现,因此他怎么判断重复,后面的map会详细说。
TreeSet(JDK1.2)
底层为红黑二叉树,实际上就是TreeMap.
不允许为null,不能重复,有序存储(顺序可以自定义)//存储空会报错
TreeSet的有序存储,存储元素时会判断他是否重复,并且自动排序,判断重复元素由compareTo()方法来实现。因此自定义类要使用TreeSet必须覆写Comparable接口。具体的排序规则可以由我们自己来定。
自定义类实现Comparable接口实例:
import java.util.HashSet;import java.util.Set;class Person implements Comparable<Person> {public String name;public String school;private int age;public Person(String name, String school, int age) {this.name = name;this.school = school;this.age = age;}public String toString() {return "[name: " + this.name + " school: " + this.school + " age: " + age + "]";}/* * 复写 .compareTo() 的规定: 当前对象大于传入对象,返回一个正数 当前对象等于传入对象,返回一个0 当前对象小于传入对象,返回一个负数 */@Overridepublic int compareTo(Person o) {if (this.age > o.age) {return 1;} else if (this.age < o.age) {return -1;} else {int i = this.name.compareTo(o.name);if (i != 0) {return i;}return this.school.compareTo(school);}}}public class Test {public static void main(String[] args) {Set<Person> list = new HashSet<>();Person per1 = new Person("andy", "XUST", 21);list.add(per1);list.add(per1);list.add(new Person("hjj", "XUST", 22));list.add(new Person("zgd", "XUST", 23));for (Person person : list) {System.out.println(person);}}} Map(JDK1.2)针对键值对,可以通过key值找到value。key不可重复,value可以重复。
常用方法:
put(k,v);存储元素
get(k);取出元素
keySet();返回所有key信息保存在set接口(key值不能重复)
Values();返回所有values信息保存在Collection集合中(value可以重复)
entrySet();将Map集合转变成Set集合
允许key,value为空,value允许多个空
内部实现:数组+链表
HashMap的初始化和扩容:
HashMap为懒加载机制,当创建HashMap的实例化对象的时候,HashMap并不会直接分配存储空间,当第一次添加元素的时候才会真正分配空间。默认容量为16,默认负载因子为0.75,当当前元素的个数大于或等于阈值(即当前数组大小*负载因子)的时候就会扩容为原大小的2倍。
HashMap一共有4个构造方法:
HashMap(); -->默认容量,默认负载因子
HashMap(int initalCapacity) -->指定容量,但是指定容量必须是2的倍数,如果不是2的倍数系统会自动将这个数转成大于这个数且最接近这个数且是二的倍数的一个数。
HashMap(int initalCapacity,float) -->指定容量,指定负载因子(一般情况下负载因子不建议指定)
HashMap(Map< ? extends Key,? extends Value>map ) 利用这个Map再构造一个HashMap对象
HashMap底层可以看作是数组(Node[] table)和链表结合组成的复合结构,数组被分为一个个桶(bucket),通过zgdxss值决定了键值对在这个数组的寻址;zgdxss值相同的键值对,则以链表形式存储,这里需要注意的是,如果链表大小超过阈值
HashMap是以<K,V>的形式存储数据,当输入一个Key值会自动生成一个Hash值,再用(HashMap数组的大小-1)&这个Hash值就是元素的存储位置,如果插入的值这个位置已经有元素了那么就会忘链表的后面插入元素,当一个链表中的元素超过八个元素并且总元素的个数超过了64个就会将这个链表树化。如果链表中元素的个数减少到6个,就又会变成链表。
TreeMap(JDK1.2)底层实现为红黑树,Key不可重复,且key不为null,输入null会抛出NullPointerException。
运用此类需要复写Comparable接口。
HashTable(JDK1.0)HashTable 是最早实现这种二元偶对象数据结构,后期的设计只是将其实现了Map接口。
HashTable和HashMap的区别:
ConcurrentHashMap 特点:线程安全,并且效率高,相当于HashMap 和HashTable的结合改良版。
JDK 1.7和JDK1.8的ConcurretHashMap实现原理的对比:
分离锁,也就是将内部进行分段(Segment),里面则是HashEntry 的数组,和 HashMap类似,zgdxss相同的条目也是以链表形式存放。
HashEntry 内部使用 volatile 的 value 字段来保证可见性,也利用了不可变对象的机制以改进利用 Unsafe 提供的底层能力,比如 volatile access,去直接完成部分操作,以最优化性能,毕竟 Unsafe 中的很多操作都是 JVM intrinsic 优化过的。
实现方式:其核心是利用分段设计,在进行并发操作的时候, 只需要锁定相应段,这样就有效避免了类似 Hashtable 整体同步的问题,大大提高了性能。
JDK1.8之后: