容器一览
集合总的来说分成Collection和Map两大部分。Collection包括List,Set和Queue三大接口。List接口下最常用的实现类是ArrayList,Vector以及和Queue接口共同实现的LinkedList;Set接口下常用的实现类是HashSet和TreeSet;Map本身就是一个大的接口,最常用的实现类是HashMap和TreeMap。值得注意的是,HashSet是套壳版的HashMap,而TreeSet是套壳版的TreeMap。
他们的特性:List有序可重复;对于Map和Set,都是不重复的,他们如果用Hash实现,就是无序的;如果用Tree实现,就是有序的。
线程安全的集合类:Vector、HashTable、和Stack。
List 和 Set 有哪些区别?和Map?
List、Set 都是继承自Collection 接口,区别主要有以下几点:
- 关于重复:list方法可以允许重复的对象,而set方法不允许重复对象。
- 关于有序:list是一个有序的容器,保持了每个元素的插入顺序,即输出顺序就是输入顺序;而set是无序容器,无法保证每个元素的存储顺序,TreeSet通过 Comparator 或 Comparable 维护了一个元素值大小顺序。
- null元素:list可以插入多个null元素,而set只允许插入一个null元素。
- 效率:List的查找效率高,插入删除元素效率低;Set相反。
- 遍历方式:List 支持for循环,也就是通过下标来遍历,也可以用迭代器(Iterator);但是Set只能用迭代器,因为他无序,无法用下标来取得想要的值。
Map本身就是一个大接口,key值必须唯一,但value可以重复。如果是HashMap实现类,元素无序;如果是TreeMap实现类,元素有序。
Map和Set的去重以及排序策略
对于 HashMap HashSet (无重复,无序)
HashMap的去重策略:
- 计算 key 的 hash 值,再通过
(数组长度 - 1) & hash得出数组下标 index; - 查看 table[index] 是否存在数据,没有数据就构造一个包含key-value的Node节点存放在这里
- 若已存在数据,说明发生了冲突(即hash值一样),判断key是否相等,若相等,用equals比较value,若不同就用新value替换旧value,若相同就不添加了,break;
- 如果key不相等,则在链表或红黑树中追加数据
HashSet的去重策略:
HashSet就是套壳版的HashMap,它表面上存的是单个数据而非键值对,其实源码里是统一拿一个final 类型的 Object 对象作为value,只起占位作用。也就是说,自身存的“单个数据”是key,value是统一的 Object,只是为了和HashMap形式上保持一致!
HashSet源码中add方法就是调用的HashMap的put方法,add(x)=put(x, Object))
从而方便全盘借鉴HashMap的去重策略,因此只研究ashMap就够了。
对于 TreeMap TreeSet(无重复,有序)
去重策略:
TreeSet借鉴的是TreeMap,直接分析TreeMap 就够了。
在TreeMap 的 add方法中可以看到,TreeMap底层的数据结构是一个红黑树,如果新增一个key进来,首先会判断这key的值在树中是否存在,若存在则替换掉,若不存在则插入其中。
排序策略:
仍然是只研究TreeMap就够了。在存入数据的时候,TreeMap按照Comprator排序。要么key所属的类实现Comparable接口,要么自定义一个实现了Comparator接口的比较器。
迭代器 Iterator 是什么?怎么使用?有什么特点?
迭代器提供一种遍历Collection 集合中元素的方法。
使用hasNext()方法作为循环条件;再用next()方法得到下一个元素;遍历过程中不能直接调用该集合的删除方法,那样会报异常,可以使用迭代器对象的remove()方法来删除集合元素。
Iterator遍历集合元素时,只能单向遍历,而ListIterator可以双向进行遍历。
Java中如何确保一个集合不能被修改
-
错误的方案1:使用final修饰
集合类的对象是引用类型,用final修饰只能保证其指向不能变,但指向的对象中的内容是可以变的
-
错误的方案2:使用Arrays.asList创建
虽然不能再add(),但是可以set(),不满足不可改变的要求
-
正确的方案:使用Collections.unmodifiableCollection()方法来创建一个只读集合
// Map集合 Map<Integer, String> map = new HashMap<>(); map.put(1, "s"); map = Collections.unmodifiableMap(map); map.put(2, "d"); // 异常: Java.lang.UnsupportedOperationException // List集合 List<String> list = new ArrayList<>(); list.add("s"); list = Collections.unmodifiableList(list); list.add("d"); // 异常: Java.lang.UnsupportedOperationException // Set集合 Set<String> set = new HashSet<>(); set.add("s"); set = Collections.unmodifiableSet(set); set.add("d"); // 异常: Java.lang.UnsupportedOperationException
ArrayList 和 Array 的区别?
- 数组可以包含基本数据类型和引用类型,但要求是同一种;ArrayList只能存引用类型,但可以是多种引用类型,因为底层的数组类型是Object(如果存基本数据类型,存的将是他们的包装类)。
- Array 是指定固定大小的,而 ArrayList 是可以自动扩容的。
- ArrayList是List接口的实现类,相比数组支持更多的方法和特性,比如比如 addAll、removeAll、iteration。
数组Array 与 列表List 之间相互转换?
String[] strs = new String[] {"aaa", "bbb", "ccc"}; List<String> list = Arrays.asList(strs); // 数组转化为list String[] array = list.toArray(new String[list.size()]); // list转化为数组
ArrayList 和 Vector的区别?
相同点:
- 都实现了List接口,都是动态数组可扩容。
不同点:
-
Vector是线程安全的,每个方法上都套上了synchronized,但也正因此性能会差一些;ArrayList是不安全的。
可通过 Collections 的 synchronizedList 方法将ArrayList变成安全的:
List<String> syncList = Collections.synchronizedList(arraylist); -
Vecto扩容时空间增加为原来的2倍;ArrayList是1.5倍。
ArrayList 和 LinkedList 的区别?
- ArrayList更擅长查询和更新,因为顺序存储的特性支持用下标随机访问。对于插入删除,效率较低,因为要移动数组内其他元素,但如果是在末尾操作,效率大体相同。
- LinkedList更擅长插入和删除,只修改指针指向即可。对于查询和更新,需要从某一端遍历,效率很低。另外,指针除了存元素值之外,还存了前后两个元素的引用,因此LinkedList要更占内存。
- ArrayList支持扩容,LinkedList不支持也不需要。二者都是线程不安全。
HashMap和HashTable的区别
1、线程安全
首先HashMap是线程不安全的,而HashTable是线程安全的,因为get和put方法都使用了synchronized关键字来修饰,通过锁机制来保证线程安全。
2、性能
既然HashTable用到了锁,那就有可能导致线程阻塞,因此性能较差。如果要线程安全又要保证性能,建议使用 JUC 包下的 ConcurrentHashMap。
3、继承的父类不同
HashMap继承的是AbstractMap类;HashTable继承的是Dictionary类。
4、初始容量和扩容尺度
HashMap 的初始容量为:16,Hashtable 初始容量为:11
HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 + 1
5、NULL的存储
Hashtable 的键和值都不允许为 null ,否则报空指针异常。
HashMap 的值可以为null,但只能有一个键为null;后续再有key=null,会覆盖之前的键值对。
www.cnblogs.com/fengzheng/p… github.com/Snailclimb/… blog.cnkj.site/Interview/I…