提到java中的容器就不得不说泛型,那什么是泛型看容器的底层代码
看这句“public interface Collection
在计算机科学中,一个收集或容器是一个分组的一些可变数目的数据项(可能为零)的是已经一些共享意义到该问题被解决并且需要到被操作时一起在某些控制方式。一般情况下,该数据项将是的 的 相同 类型 或, 在语言支持继承,衍生自一些共同的祖先类型。一个集合是一个概念,适用于抽象数据类型,并且它没有规定一个具体的实施作为一个具体的数据结构,虽然经常有是一个常规的选择(见集装箱的 类型理论 讨论)。
Collection是一个接口类型,它继承与Iterable这个接口,Collection顾名思义
是集合的意思,他也算是集合的老父亲一种,看前面的种类图就了解了set,list集合接口又都继承它,它其中一些常用的方法
方法 | 说明 |
---|---|
Boolean add(Object element) | 增加元素到容器中 |
Boolean remove (Object element) | 从容器中移除元素 |
Boolean contains(Object element) | 容器中是否包含该与元素 |
int sixe() | 容器中元素数量 |
Boolean isEmpty() | 容器是否为空 |
void clear() | 情况容器中所有元素 |
Iterator iterator() | 获得迭代器用于遍历元素 |
boolean containsAll(Collection c) | 本容器中是否包含c容器的所有元素 |
boolean addAll(Collection c) | 将容器c中的所有元素增加到本容器中 |
boolean removeAll(Collection c) | 移除本容器中和容器c中都包含的元素 |
boolean retainAll(Collection c) | 去本容器和c容器中都包含的元素,移除非交集元素 |
Object [] toArray() | 转化成Obiect数组 |
Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致。
Set容器特点:无序、不可重复。无序指Set中的元素没有索引,我们只能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;甚至,Set中也只能放入一个null元素,不能多个。
HashSet是采用哈希算法实现,底层实际是用HashMap实现的(HashSet本质就是一个简化版的HashMap),因此,查询效率和增删效率都比较高。HashSet核心代码就一句public HashSet() {map = new HashMap<>(); }这个是句话的意思是实现一个新的HashSet对象就创建一个新的HashMap对象,而往set中加入元素,本质就是把这个元素作为key加入到了内部的map中,value值是方法中定义的一个常量”。 由于map中key都是不可重复的,因此,Set天然具有“不可重复”的特性。
简单了解一下HashSet的一些方法,他其实就是实现Set接口中的方法,会使用就行,主要记住他的特点,无序,不可重复
public static void main(String[] args) {
Set<String> s = new HashSet<>();
s.add("1");
s.add("65");
s.add("3");
System.out.println(s);
s.add(null);
System.out.println(s);
s.add(null);
System.out.println(s);//Set集合的无序性
Set<String> s1 = new HashSet<>();
s1.add("3");
s1.add("78");
s.addAll(s1);
System.out.println(s);//看出Set集合的不可重复性
}
TreeSet就是树形set,他其实在内部定义了一个TreeMap,底层核心代码就这一句 public TreeSet() { this(new TreeMap
注意:
(1) 由于是二叉树,需要对元素做内部排序。 如果要放入TreeSet中的类没有实现Comparable接口,则会抛出异常:java.lang.ClassCastException。
(2) TreeSet中不能放入null元素。
List集合其实和数组有一点很相似,它可以通过下标(索引)来实现元素的查找,而数组和List的最大区别就是,数组不可扩容,List是可以扩容的。
List接口也是继承Collection接口的它也会具有collection的所有方法,但是它其中根据自己的特性重写或者新加了一些方法
方法 | 作用 |
---|---|
void add(int index,Object element) | 在指定位置插入元素,以前元素全部后移一位 |
Object set(int index ,Object element) | 修改指定位置的元素 |
Object get(int index) | 返回指定位置的元素 |
Object remove(int inedex) | 删除指定位置的元素,后面的元素全部向前移一位 |
int indexOf(Object o) | 返回第一个匹配元素的索引,如果没有该元素,返回-1. |
int lastIndexOf(Object o) | 返回最后一个匹配的元素的索引,如果没有该元素,返回-1. |
ArrayList底层是用数组实现的存储。 特点:查询效率高,增删效率低,线程不安全。我们一般使用它。
ArrayLiat的一些常用的方法
public static void test01(){
List<String> list01 = new ArrayList<>();
list01.add("aa");
list01.add("bb");
list01.add("cc");
List<String> list02 = new ArrayList<>();
list02.add("ss");
list02.add("dd");
list02.add("cc");
list01.addAll(list02);
System.out.println(list01);
System.out.println(list02);
System.out.println(list01.get(1));// 应该是“bb”
System.out.println(list01.lastIndexOf("aa"));
//list02.removeAll(list01);//去掉list02中和list01相同部分
list01.retainAll(list02);//
System.out.println(list01);
System.out.println(list02);
}
如果你自己看完ArrayList,或者我这个简陋的手工实现。你可以根据自己的理解也编写出自己的ArrayList类,我觉得就很不错了,源代码我就不在这放了,不水字数了。强烈建议读者去看一看(模仿优秀的东西,是自己也变得优秀。这也是学习的一个好途径)
下面是自己写代码,有错欢迎各位大佬指导,不喜勿喷
补充:在1.8的jdk中
/**
* @author zs
* List是有序的可重复的容器
* 有序:List每个元素都有索引标记
* 可重复:List允许加入重复的元素。跟确切的将List通常允许满足e1.equals(e2)的元素重复加入容器
* List接口常用的实现类 ArrayList ,LinkedList,Vector(线程安全)
* ArrayList特点:查询效率高,增删效率低,线程不安全
*/
public class Test_MyList <E>{
private Object[] elementData;
private int size;
private static final int DEFALT_CAPACITY = 10;
public Test_MyList(){
//初始化一个大小位10的数组容器
elementData = new Object[DEFALT_CAPACITY];
}
public Test_MyList(int capacity){
//capacity 容器 初始化一个大小为capacity的数组容器
if (capacity<0){
throw new RuntimeException("容器的容量不能位负数");
}else if (capacity==0){
elementData = new Object[DEFALT_CAPACITY];
}else {
elementData = new Object[capacity];
}
}
public void add(E element){
//加进元素
//什么时候扩容??
if(size==elementData.length){
//怎么扩容 本质是将一个小的装满的数组拷贝到另一个大的数组
Object[] newArray = new Object[elementData.length+(elementData.length>>1)];
System.arraycopy(elementData,0,newArray,0,elementData.length);
elementData = newArray;
//>> 右移 位移的优先级小于运算符的优先级
}
elementData[size++] = element;
}
//get方法
public E get(int index){
checkRange(index);
return (E)elementData[index];
}
//set方法
public void set(E element,int index){
checkRange(index);
elementData[index]=element;
}
//checkRange 判断索引合法性 //因为get,set这些方法中都要判断索引的合法性,所以就拿出来单独写一个方法
public void checkRange(int index){
//索引合法判断[0,size] 10 0-9
if(index<0||index>size-1){
//不合法
throw new RuntimeException("索引不合法"+index);
}
}
//remove 移除
//移除整个对象
public void remove(E element){
//element,将它和所有元素挨个比较,获得第一个比较位true的,返回
for(int i=0;i<size;i++){
if(element.equals(get(i))) {
remove(i);//移除一个元素
}
}
}
// 移除一个元素,移除元素实际也是数组的拷贝
public void remove(int index){
checkRange(index);
//a,b,c,d,e,f
//a,b,d,e,f
int numMoved =elementData.length-index-1;
if (numMoved>0){
System.arraycopy(elementData,index+1,elementData,index,numMoved);
}
elementData[--size]=null;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i=0 ;i<size;i++){
sb.append(elementData[i]+",");
}
sb.setCharAt(sb.length()-1,']');
return sb.toString();
}
public static void main(String[] args) {
Test_MyList<String> list = new Test_MyList<String>(6);
for(int i=0;i<10;i++){
list.add("zhou"+i);
}
list.remove(3);
System.out.println(list);
}
}
这个代码里面最难理解其实就是一个方法的使用,数组的拷贝arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
参数含义:
(原数组,原数组的开始位置,目标数组目标数组的的开始位置,拷贝的个数)
你其实知道了这个也就能看懂了,,如何还不太清楚它的使用,或者想更加了解深入数组拷贝
补充:在1.8的jdk中,ArrayList的add主要方法,它很多方法的来回调用来实现add方法,这当然是跟好的,代码从来都是写模块比写方法重要,一个方法就是一个模块。他把add的方法实现编程就很多的模块的叠加,很简单一个例子,以一万个小部件拼出来的高达模型,肯定要比用一千个拼出来的高达模型要牢靠,也就是容错率强。言归正传,其实他的这个add方法的实现,归根揭底少不了使用System.arraycopy这个方法,他在整个add模块实现都没体现这个方法的使用,但是他用array.copyof
()这个方法实现的新老数数组的交替,你在打开array.copyof()这个方法的底层源码,一看,还是使用的System.arraycopy这个方法拷贝新老数组。
Vector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记
他最核心的也就是这个synchronized同步标记了,它实现了线程安全,如果要了解它,就肯定要了解多线程呀,锁呀,在这不深入理解,等博主有时间再写一篇关于多线程的。你可以看看这个synchronized
Vetor常用方法和Arraylist差不多 ,这里就不进行代码实践了,它主要的区别就是需要线程安全是用Vecto; 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。
LinkedList底层用双向链表实现的存储。特点查询效率低,增删效率高,线程不安全。
双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。
有关Map集合讲解