java中的集合(新手进)

一、java集合

1.为什么要有java集合?有数组能存多个不是足够了吗?

答:

面向对象语言中,需要经常一起操作多个对象,虽然数组也可以存储对象,但数组长度是固定的,不可变。而集合的长度是可变的。而且数组只能存储一类对象,而集合可以将多种对象存储到一起。

2.集合的特点?

答:

1.只能存储对象。(如果存储int i = 1;存进去会自动装箱成Integer类)

2.集合长度是可变的。(在初始化时不需要设定长度)

3.可以存储多种类型的对象

二、以下是针对上图,对每一个集合做简单的赘述。

首先要知道,java集合总共有两种,单列集合和双列集合。单列集合(collection)存储对象,双列集合(Map)存储键值对。

附上简单版集合框架图

 

 

1.collection接口:

1.1.特点:单列集合的根接口,

1.2.所有collection的子孙类,都能使用collection的方法。

Collection s = new ArrayList();//(接口只能实例化它的子类)
Object o=new Object();
		
s.add(o);//添加一个元素,添加成功返回true,失败返回false
		
s.remove(o);//移除一个或多个元素,成功true,失败false
		
s.clear();//清空集合,无返回值
		
s.contains(o);//检查是否包含元素,boolean
		
s.isEmpty();//检查集合是否为空,boolean
		
int a = s.size();//返回集合中元素的个数
		
Collection t = new ArrayList();
s.addAll(t);//将另一个单列集合t的内容全部添加到当前集合s中,booelan
		
s.removeAll(t);//移除s集合中也在t集合中的全部元素,boolean
		
s.containsAll(t);//检查s是否包含t的全部元素,boolean
		
s.retainAll(t);//将s与t的交集保存回s中,将s的其它元素移除

1.3集合的遍历(所有单列集合均能如此遍历)

Collection s = new ArrayList();//(接口只能实例化它的子类)
s.add(1);
s.add(2);
		
//1.将集合转成数组
Object[] array = s.toArray();
for(int i=0;i<array.length;i++) {
	System.out.println(array[i]);
}
		
//2.使用迭代器
Iterator iterator = s.iterator();
while(iterator.hasNext()) {
	System.out.println(iterator.next());
}

//3.forEach
for(Object o:s) {
	System.out.println(o);
}

 

 

2.List接口

2.1.特点:

    List里的元素都是有序的(先存的在List里的位置也靠前),

    List里的元素是可以重复的,你可以添加两个一模一样的元素进去

2.2.独有方法(只要是List均可使用)

   

List s = new ArrayList();
s.add(0,10);//在第0个位置添加一个Integer对象10
s.add(1,20);//在第1个位置添加一个Integer对象20		
//s.add(3,30);//跨数添加,报错
s.add(1,30);//在已有元素的位置上添加新元素,会把原来及往后的元素挤到后面,此时[10,30,20]
		
s.remove(1);//移除位置1上的元素,后面的元素会往前,返回被移除的元素
		
s.get(1);//得到在指定位置的元素并返回
		
s.set(0, 100);//将第0个元素设为100,返回以前在此位置的元素

2.3独有遍历机制(只要是list均可这样遍历)

//1.特有迭代器
ListIterator listIterator = s.listIterator();
while(listIterator.hasNext()) {//从前往后打印
	System.out.println(listIterator.next());
}
		
while(listIterator.hasPrevious()) {//从后往前打印
//注意:必须先让指针从前往后遍历完,再从后往前,否则打印为空
	System.out.println(listIterator.previous());
}
	
//2.for循环
for(int i=0;i<s.size();i++) {
	System.out.println(s.get(i));
}

2.4数组转list

                Integer a[] = {1,2,3,4,5};
		List<Integer> asList = Arrays.asList(a);
		
		for(Integer s:asList) {
			System.out.println(s);
		}
		
		//注意:使用aslist得到的list,是不能进行add和remove的(仅可读的集合)
		//asList.add(10); //java.lang.UnsupportedOperationException
		
		//如果想要操作此集合,请作为参数传入到新建的ArrayList中
		ArrayList<Integer> as = new ArrayList<Integer>(asList);
		as.add(6);//成功添加

 

3.set接口

3.1.特点:set里面不能存放相同的元素(利用这一点,我们可以实现对集合的去重),里面存放的元素也是无序的。

3.2.没有什么独有的方法

3.3没有什么独有的遍历方式

 

 

4.Queue接口

4.1.特点:队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。通常以 FIFO(先进先出)的方式排序各个元素。

4.2.独有的方法

Queue<Integer> s = new LinkedList<>();
		
//不推荐使用Queue提供的add()方法添加,因为可能会抛出异常
s.offer(1);//将元素插入到队列末端
s.offer(2);
s.offer(3);
		
//此时:  头[1,2,3]尾
s.peek();//获取队列头部元素1
		
s.element();//获取队列头部元素1
		
//不推荐使用Queue提供的remove()方法添加,因为可能会抛出异常
s.poll();//获取并移除队列头部元素1
//此时:	头[2,3]尾

 

 

5.ArrayList(线程不安全,能存且可存多个null)

5.1.特点: 没什么特点,有list的基本功能。底层是用数组实现的,增删慢,查询快

5.2.比较重要的独有方法

//构造方法ArrayList(Collection<? extends E> c) 
//构造一个包含指定 collection 的元素的列表,其实相当于s2.addAll(s);
List s2 = new ArrayList(s);

5.3.没什么独有的遍历方式

5.4所有集合实现线程安全,均可使用以下方法创建

List list = Collections.synchronizedList(new ArrayList()); 

 

 

 

6.LinkedList(线程不安全,能存且能存多个null)

5.1.特点,除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。底层是用链表实现的,增删快,查询慢。

5.2.独有方法

5.3.独有遍历方式

Iterator<Integer> des = s.descendingIterator();//获取逆序迭代器
while(des.hasNext()) {
	System.out.println(des.next());//逆序打印
}

 

 

 

7.Vector(线程安全,能存且能存多个null)

7.1.特点:Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度
的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。

7.2.比较重要的方法

Vector<Integer> vector = new Vector<>(10);
vector.add(1);
		
System.out.println(vector.capacity());//10
System.out.println(vector.size());//1
		
for(int i=0;i<10;i++) {//再添加10个,使集合中的元素的数目大于目前集合数组的长度
	vector.add(i);
}
		
System.out.println(vector.capacity());//20,容量增长100%
System.out.println(vector.size());//11

 

8.Stack(线程安全,能存且能存多个null)

8.1.特点: Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展

8.2.重要方法

Stack<Integer> s = new Stack<Integer>();
s.push(2);
s.push(4);
s.push(6);

//底[2,4,6] 顶 ,顶的位置为1,底的位置为3

System.out.println(s.peek());//栈顶元素是6
System.out.println(s.search(2));//2在栈底,位置是3
		
while(!s.empty()) {//全部出栈 6,2,4
	System.out.println(s.pop());
}
	
System.out.println(s.isEmpty());//true  empty()与collection的isEmpty()方法是一样的

 

9.HashSet(线程不安全,能存但最多只有一个null)

9.1.特点:

①拥有基本的set特性,不允许存放相同的对象(通过hashCode()和equals()方法判定是否为同一对象),存放的对象也是无序的,没有索引,也无法保证存入和取出的顺序一致。

②通过hash值存储对象到散列单元。所以,查询快。hash值是由对象导出的一个整数值。在Object中有一个hashCode方法来得到hashCode。基本上,每一个对象都有一个默认的hashCode,其值就是对象的地址值。但也有一些类重写了hashCode()方法,比如String对象是通过对内容计算得到(所以内容相同hashCode就相同)。

Hash值可能与HashCode相等(HashTable就使用Key的hashCode直接作为Hash值),也可能不相等(HashMap使用Key的hashCode,高16位不变,高16位与低16位做异或运算作为Hash值,这个称为Hash算法,Hash算法将元素尽可能均匀地平铺在数组上)

③HashMap实现机制,底层是数组+链表。而HashSet底层,就是HashMap。

举例:

Java默认的散列单元大小全部都是2的幂,初始值为16(2的4次幂)。则开辟一个容量为16的数组(1~16),

存放一个hash值为24342的对象时,通过计算index,就把它放到第数组的第6个位置,(计算index方法:index = hash值 & (map.length – 1))。

[这里解释一下,有些人说通过取余来确定位置,其实通过取余来确定位置的并不是HashMap,而是HashTable(计算index的方法:index = (hash值 & 0x7FFFFFFF) % table.length))

因为扩容的时候是2的n次方进行扩容,Hash值在2的n次方进行求余运算的结果都一样,但是&运算要快得多,而且因为扩容倍数的特殊性,导致扩容后不需要重新计算元素在数组中的位置,只需要判断Key的hash值多出来的那一位是0还是1,如果是0,新表中元素的位置和旧表一样,如果是1,新表中键值对的位置等于旧表的位置+旧表的长度]

如果,再存放一个hash值为24358的对象,再通过计算index,发现同样要落在数组第6个位置(即发生了hash碰撞),则在数组的第6个位置生成一个链表,将hash值为24358的对象放到链表头,指向刚才的hash值为24342的对象。(至于为什么要把新的放到链表头,可能设计师觉得,你新加进来的,被使用的可能性大)

如果,再存放一个一模一样的对象,在数组的第6个位置的链表中发现有一个老数据与新数据equals()=true的话,这个新数据将被视为已经加入,而不再重复丢入链表。

如果,16条链表中的75%链接有数据的时候(加载因子默认为0.75),HahSet则开始重新散列,也就是将原来的散列结构全部抛弃,重新开辟一个散列单元大小为32(2的5次幂)的散列结果,并重新计算各个数据的存储位置。因为空间越大,取余数相同的情况就越小,也减少了hash碰撞。

④优点:

查找和增删都很快,知道了HashSet的添加对象的机制后,查找的道理一样。直接根据数据的hash码(散列码)和计算规则后,就得到了所在数组的位置,然后再查找链表中是否有这个数据即可(每个链表存储的数据很少,所以迭代起来也很快)。

⑤缺点:

但是hashSet增删的高效率是通过花费大量的空间换来的:HashSet这种算法会建立许多无用的空间。

9.2独有方法

Set a = new HashSet();
for(int i=1;i<16;i++) {//按顺序存入1~15的字符串形式
	a.add(i+" ");
}
Iterator iterator = a.iterator();
//不一定按存入顺序打出
while(iterator.hasNext()) {//11 10 9 8 7 6 5 4 3 2 1 15 14 13 12
	System.out.println(iterator.next());
}

 

 

 

10.LinkedHashSet(线程不安全,能存但最多只有一个null)

10.1.特点:LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。

Set a = new LinkedHashSet();
for(int i=0;i<16;i++) {
	a.add(i+" ");
}
		
//按顺序打印
for(Object o:a) {//0 1 2 3 4 ...
	System.out.println(o);
}

 

 

 

10.TreeSet(线程不安全,存null将会抛异常)

10.1.特点:一个能够对元素进行自动排序的set集合

Set a = new TreeSet();
Random random = new Random();
for(int i=0;i<16;i++) {//添加16次随机数(1~20),但不一定添加16个(有可能随机数重复)
	a.add(random.nextInt(20));
}
	
//打印
for(Object o:a) {//1 4 6 10 11 12 13 14 15 17
	System.out.println(o+" ");
}

10.2.独有方法(自定义排序规则)

 

 

 

11.Map接口

11.1特点: 双列集合的根接口,存储键值对(key-value),key不可重复,但可以为null(最多一个null),value可以重复

11.2独有方法(只要是Map均可使用)

Map s = new HashMap();
		
//注意,Map接口(双列集合))没有add()方法
s.put("1", 16);
s.put("2",98);
System.out.println(s.get("2"));//98

11.3独有遍历方式

                //1.keySet	获取所有key值
                Set keySet = map.keySet();
		//可以使用3种对set的遍历方式遍历
		//1.1转数组
		Object[] array = keySet.toArray();
		for(int i=0;i<array.length;i++) {
			System.out.println("key="+array[i]+" value="+map.get(array[i]));
		}
		//1.2 forEach
		for(Object o:keySet) {
			System.out.println("key="+o+" value="+map.get(o));
		}
		//1.3 Iterator
		Iterator iterator = keySet.iterator();
		while(iterator.hasNext()) {
			String s = (String) iterator.next();
			System.out.println("key="+s+" value="+map.get(s));
		}
		
		//2.values 只能获取所有value值(无法通过value得到key)
		Collection values = map.values();
		//同样可以使用3种遍历方式
		for(Object  s:values) {
			System.out.println(s);
		}
		
		//3.entrySet 每一个键值对都是一个Entry
		Set entrySet = map.entrySet();
		for(Object e:entrySet) {
			Entry en = (Entry) e;
			System.out.println("key="+en.getKey()+" value="+en.getValue());
		}

 

 

12.HashMap(线程不安全,key最多一个null,value可以多个为null)

12.1.特点:没什么特点,拥有Map的全部功能。值得一提的是,HashSet的底层就是通过HashMap来实现的。即HashSet存值,实际上是新建了一个HashMap,将值存到key中。存入数据的时候,并不能保证存储的顺序。

                Map map = new HashMap();
		
		for(int i=0;i<20;i++) {//添加20个键值对,键为0~19,值为键的平方,均为字符串形式
			map.put(i+" ", " "+i*i);
		}
		
		Set keySet = map.keySet();//并不按顺序输出 
		Object[] array = keySet.toArray();
		for(int i=0;i<array.length;i++) {
			System.out.println("key="+array[i]+" value="+map.get(array[i]));
		}

 

 

13.LinkedHashMap(线程不安全,key最多一个null,value可以多个为null)

没错,加了Linked,就是表示它底层是链表,它通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序。

LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序

 

 

14.TreeMap(线程不安全,key不能为null,值可以为null)

没错,TreeMap能够对元素自动排序,实现顺序输出。

如果key是数字,默认按key值的升序输出。如果key是字符串,则按照A>...>Z>a>...>z的Ascll值输出。

		Map map = new HashMap();
		Random random = new Random();
		for(int i=0;i<20;i++) {
			map.put(random.nextInt(20), i*i);
		}
		
		Set keySet = map.keySet();//按key的顺序升序输出
		Object[] array = keySet.toArray();
		for(int i=0;i<array.length;i++) {
			System.out.println("key="+array[i]+" value="+map.get(array[i]));
		}

如果key是其他类型,则需要定义好排序规则,否则会报错。

 

 

15.HashTable(线程安全,值和key都不能为null)

HashMap和HashTable基本上是等价的,至于为什么还是要单独拿出来讲,因为面试实在问太多关于这两个类了。以下是转载的大佬的总结。

HashMap和Hashtable的区别

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值