面试总结1—集合问题

面试总结1—集合问题

1.基本概念

​ 集合主要的功能是用来保存对象的,不同的集合类有不同的特性。比如LIST存储的对像是有序且可重复的,Set是无序且不可重复的。因为集合是对数组做的封装,所以,数组永远比任何一个集合要快

​ JAVA将集合类划分两大类:

  1. Collection接口:一个独立元素的序列,这些元素服从一些规则。List,Set,Queue都继承了这个接口。
  2. Map接口:一组成对的“键值对”对象,允许通过键来找值。

问题1:Collection接口和Collections类的区别:

​ Collection是集合类的根接口,Collection 表示一组对象,这些对象也称为 collection 的元素。定义在不同Collection的子接口(List,Set,Queue)的元素有不同的规则。

​ Collections是集合类的工具类,里面定义了一些静态方法用来操作集合。他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作 。就好像Arrays类是用来操作数组的一样。

通过一个add例子来查看

public class AddingGroups {
public static void main(String args[]){
	Collection<Integer> collection =new ArrayList<Integer> (Arrays.asList(1,2,3,4,5));
	Integer[] modiy={6,7,8,9};
	collection.addAll(Arrays.asList(modiy));
	
	Collections.addAll(collection,10,11,12,13);//比Collection.addAll()快
	Collections.addAll(collection, modiy);
	List<Integer> list=Arrays.asList(14,15,16,17);
	/*
	 * 用Arrays.asList做输出的话 因为其底层表示的是数组,因此不能改变大小
	 * list.add(1,99)抛错!
	 * 可以通过new ArrayList(Arrays.asList(14,15,16,17));这样就能够改变list结构
	 */
	list.set(1, 99);
	for(Integer i:collection)
		System.out.println(i);
	for(Integer ii:list)
		System.out.println(ii);
}
}

/*
Collections.addAll(Collection<? super T> c,T... element)
java.util.Collecton.addAll(Collection<? extends E> c)
	通过上面可以看出Collection只能添加一个Collection对象		    Collections.addAll()操作的参数是可变的T,
 	官方API中写道:将所有指定元素添加到指定 collection 中。可以分别指定要添加的元素,或者将它们指定为一个数组。此便捷方法的行为与 c.addAll(Arrays.asList(elements)) 的行为是相同的,但在大多数实现下,此方法运行起来可能要快得多。
 */

2.List接口(有序可重复)

public interface List<E> extends Collection<E> ,Iterable<E>

​ 从List接口的定义中我们可以看出继承自Collection接口和Iterable接口(迭代器)

有两种类型的List:

  1. ArrayList:基于数组实现
  2. LinkedList:双向循环链表的数据结构
  3. Vector:现在基本不用了

问题2:ArrayList和LinkedList的区别

​1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

相同点:都不是线程安全的。

问题3:ArrayList和Vector的区别(是否有序、是否重复、数据结构、底层实现)

ArrayList和Vector都实现了List接口,他们都是有序集合,并且存放的元素是允许重复的。它们的底层都是通过数组来实现的,因此列表这种数据结构检索数据速度快,但增删改速度慢。

而ArrayList和Vector的区别主要在两个方面:

第一,线程安全。Vector是线程安全的,而ArrayList是线程不安全的。因此在如果集合数据只有单线程访问,那么使用ArrayList可以提高效率。而如果有多线程访问你的集合数据,那么就必须要用Vector,因为要保证数据安全。

第二,数据增长。ArrayList和Vector都有一个初始的容量大小10,当存储进它们里面的元素超过了容量时,就需要增加它们的存储容量。ArrayList每次增长原来的0.5倍,而Vector增长原来的一倍。ArrayList和Vector都可以设置初始空间的大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法

问题4:如何遍历List集合

方式一:使用迭代器

​ 由List的继承可知List继承了Iterable接口,在API中说到:实现这个接口允许对象成为 “foreach” 语句的目标。java.lang.Iterable

其中的方法:
Iterator<T> iterator()
返回一个在一组 T 类型的元素上进行迭代的迭代器。

​ java.util.Iterator :对 collection 进行迭代的迭代器。迭代器是一个对象,他的工作是遍历并选择序列中的对象。

其中有三个方法:
boolean hasNext():如果仍有元素可以迭代,则返回 true。
E next() :返回迭代的下一个元素。
void remove():从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。

使用迭代器遍历

List<String> list=new ArrayList<String>(Arrays.asList("a","b","c"));
		Iterator<String> it=list.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
//iterator有个子接口ListIterator比Iterator接口更加强大可以双向移动,但是只能用来遍历List集合。

这里注意一个问题如果我们想遍历的时候将list集合中的元素删除,只能通过迭代器的方式,其他方式都不可以会报错!

List<String> list=new ArrayList<String>(Arrays.asList("a","b","c"));
		Iterator<String> it=list.iterator();
		while(it.hasNext()){
			it.next();
            it.remove();
		}

方式二:foreach语句(只有实现了Iterable接口才能使用)

List<String> list=new ArrayList<String>(Arrays.asList("a","b","c"));
		for(String s:list){
			System.out.println(s);
		}

方式三:使用for循环

List<String> list=new ArrayList<String>(Arrays.asList("a","b","c"));
for(int i=0;i<list.size();i++){
	System.out.println(list.get(i));
		}

3.Set集合(无序且不可重复)

​ Set里面不允许有重复的元素,所谓重复,即不能有两个相等(注意,不是仅仅是相同)的对象 ,即假设Set集合中有了一个A对象,现在我要向Set集合再存入一个B对象,但B对象与A对象equals相等,则B对象存储不进去。所以,Set集合的add方法有一个boolean的返回值,当集合中没有某个元素,此时add方法可成功加入该元素时,则返回true,当集合含有与某个元素equals相等的元素时,此时add方法无法加入该元素,返回结果为false。Set取元素时,没法说取第几个,只能以Iterator接口取得所有的元素,再逐一遍历各个元素。

有两种类型的Set:

  1. HashSet:采用hash表(实际上是一个HashMap实例)实现
  2. TreeSet:将元素存储在红—黑树数据结构中
  3. LinkedHashSet (extends HashSet):也是一个Hash表,但同时维护了一个双链表来维护插入顺序。(插入顺序与读取顺序一致

问题5:HashSet、TreeSet、LinkedHashSet的区别

​ HashSet是采用hash表来实现的。其中的元素没有按顺序排列,add()、remove()以及contains()等方法都是复杂度为O(1)的方法,存储速度快。不是同步的,集合中的元素可以为null,但只能有一个。

​ TreeSet是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是add()、remove()以及contains()等方法都是复杂度为O(log (n))的方法。****

​ LinkedHashSet介于HashSet和TreeSet之间。它也是一个hash表,但是同时维护了一个双链表来记录插入的顺序。基本方法的复杂度为O(1)。LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。

有关于区别:https://www.cnblogs.com/wl0000-03/p/6019627.html这篇文章很详细。

问题6:遍历Set

​ 和遍历List一样三种方式!

4.Map集合(键值对)

​ Map与List和Set不同,它是双列的集合,其中有put方法,定义如下:put(obj key,obj value),每次存储时,要存储一对key/value,不能存储重复的key,这个重复的规则也是按equals比较相等。取则可以根据key获得相应的value,get(Object key)返回值为key 所对应的value。另外也可以获得所有的key的集合map.keySet(),还可以获得所有的value的结合(map.values()),还可以获得key和value组合成的Map.Entry对象的集合(map.entrySet())。

有两种类型的Map:

  1. HashMap:基于哈希表的Map接口的实现。
  2. TreeMap:基于红黑树的Map实现。
  3. LinekedHashMap extends HashMap:Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。

问题7:HashMap、TreeMap、LinkedHashMap、HashTable的区别

​ (1)HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为null,不允许多条记录的值为null。HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要同步,可以用Collections.synchronizedMap(HashMap map)方法使HashMap具有同步的能力。

​ (2)Hashtable与HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,然而,这也导致了Hashtable在写入时会比较慢。

​ (3)LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的。在遍历的时候会比HashMap慢。有HashMap的全部特性。

​ (4)TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。当用Iteraor遍历TreeMap时,得到的记录是排过序的。TreeMap的键和值都不能为空。

问题8:如何遍历Map集合

public class MapOfList {
	public static Map<Integer, List<? extends String>> map=new HashMap<Integer,List<? extends String>>();
	static{
		map.put(1, Arrays.asList("1a","1b","1c"));//这里的值是List类型的容器。
		map.put(2, Arrays.asList("2d","2e","2f"));
		map.put(3, Arrays.asList("3g","3h","3i"));
		map.put(4, Arrays.asList("4j","4k","4l"));
	}
	public static void main(String args[]){
		//方式一:在for-each循环中遍历keys或values。
		for(Integer in:map.keySet()){
			System.out.println(in+"Integer has:");
			for(String str:map.get(in))
				System.out.println("     "+str);
		}
		
		//方式二:foreach常见方法
		for(Map.Entry<Integer,List<? extends String>> ent:map.entrySet()) {
			System.out.println("key:"+ent.getKey()+"-values:"+ent.getValue());
		}
		//方式三:迭代器
		Set hs=map.entrySet();
		Iterator it=hs.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
		//方式四:通过键查找值(效率低)
		for (Integer key : map.keySet()) { 
			  List<? extends String> value = map.get(key); 
			  System.out.println("Key = " + key + ", Value = " + value);
		}
		
	}
}

注意:在Map集合中允许值对象为null,因此查看是否存在键对象应该用cotainsKey而不是get方法。

猜你喜欢

转载自blog.csdn.net/qq_38238296/article/details/87869690