Java集合面试题(2021最新版)

您所在的位置:网站首页 java集合选择题 Java集合面试题(2021最新版)

Java集合面试题(2021最新版)

#Java集合面试题(2021最新版)| 来源: 网络整理| 查看: 265

前言

马上到今年的金三银四了,又是跳槽的好季节,准备跳槽的同学都摩拳擦掌准备大面好几场,本次小编为大家准备了精选的 Java 集合面试题,快来查漏补缺吧。

小编分享的这份Java后端开发面试总结包含了JavaOOP、Java集合容器、Java异常、并发编程、Java反射、Java序列化、JVM、Redis、Spring MVC、MyBatis、MySQL数据库、消息中间件MQ、Dubbo、Linux、ZooKeeper、 分布式&数据结构与算法等25个专题技术点,都是小编在各个大厂总结出来的面试真题,已经有很多粉丝靠这份PDF拿下众多大厂的offer,今天在这里总结分享给到大家!【已完结】

完整版Java面试题地址:2021最新面试题合集集锦。

| 序号 | 专题 | 内容 | 链接 |

|:----|:----|:----|:----|

| 1 | 中间件 | Java中间件面试题(2021最新版) | https://cloud.tencent.com/developer/article/1810657 |

| 2 | 微服务 | Java微服务面试题(2021最新版) | https://cloud.tencent.com/developer/article/1811218 |

| 3 | 并发编程 | Java并发编程面试题(2021最新版) | https://cloud.tencent.com/developer/article/1812085 |

| 4 | Java基础 | Java基础知识面试题(2021最新版) | https://cloud.tencent.com/developer/article/write/1812831 |

| 5 | Spring Boot | Spring Boot面试题(2021最新版) | https://cloud.tencent.com/developer/article/1813377 |

| 6 | Redis | Redis面试题(2021最新版) | https://cloud.tencent.com/developer/article/1814536 |

| 7 | Spring MVC | Spring MVC面试题(2021最新版) | https://cloud.tencent.com/developer/article/1814561 |

| 8 | Spring Cloud | Spring Cloud面试题(2021最新版) | https://cloud.tencent.com/developer/article/1814682 |

| 9 | MySQL优化 | MySQL优化面试题(2021最新版) | https://cloud.tencent.com/developer/article/1814683 |

| 10 | JVM | JVM性能调优面试题(2021最新版) |https://cloud.tencent.com/developer/article/1814684 |

| 11 | Linux | Linux面试题(2021最新版) | https://cloud.tencent.com/developer/article/1814821 |

| 12 | Mybatis | Mybatis面试题(2021最新版) | https://cloud.tencent.com/developer/article/1814879 |

| 13 | 网络编程 | TCP,UDP,Socket,Http网络编程面试题(2021最新版) | https://cloud.tencent.com/developer/article/1814881 |

| 14 | 设计模式 | 设计模式面试题(2021最新版) |https://cloud.tencent.com/developer/article/1816736 |

| 15 | 大数据 | 大数据面试题100道(2021最新版) |https://cloud.tencent.com/developer/article/1818351|

| 16 | Tomcat | Tomcat面试题(2021最新版) | https://cloud.tencent.com/developer/article/1818417 |

| 17 | 多线程 | 多线程面试题(2021最新版) | https://cloud.tencent.com/developer/article/1818432 |

| 18 | Nginx | Nginx\_BIO\_NIO\_AIO面试题(2021最新版) | https://cloud.tencent.com/developer/article/1818676 |

| 19 | memcache | memcache面试题(2021最新版) | https://cloud.tencent.com/developer/article/1819122 |

| 20 | java异常 | java异常面试题(2021最新版) | https://cloud.tencent.com/developer/article/1819397 |

| 21 | Java虚拟机 | Java虚拟机面试题(2021最新版) | https://cloud.tencent.com/developer/article/1820065 |

| 22 | Java集合 | Java集合面试题(2021最新版) | https://cloud.tencent.com/developer/article/1820151 |

| 23 | Git常用命令 | Git常用命令(2021最新版) | https://cloud.tencent.com/developer/article/write/1820273 |

| 24 | Elasticsearch | Elasticsearch面试题(2021最新版) | https://cloud.tencent.com/developer/article/1820285 |

| 25 | Dubbo | Dubbo面试题(2021最新版) | https://cloud.tencent.com/developer/article/1820302 |

一、集合容器概述1. 什么是集合集合就是一个放数据的容器,准确的说是放数据对象引用的容器 集合类存放的都是对象的引用,而不是对象的本身 集合类型主要有3种:set(集)、list(列表)和map(映射)。 2. 集合的特点

集合的特点主要有如下两点:

集合用于存储对象的容器,对象是用来封装数据,对象多了也需要存储集中式管理。 和数组对比对象的大小不确定。因为集合是可变长度的。数组需要提前定义大小 3. 集合和数组的区别数组是固定长度的;集合可变长度的。 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。 4. 使用集合框架的好处容量自增长; 提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量; 可以方便地扩展或改写集合,提高代码复用性和可操作性。 通过使用JDK自带的集合类,可以降低代码维护和学习新API成本。5. 常用的集合类有哪些?

Map接口和Collection接口是所有集合框架的父接口:

Collection接口的子接口包括:Set接口和List接口 Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等 Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等 List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等6. List,Set,Map三者的区别?7. 集合框架底层数据结构8. 哪些集合类是线程安全的?Vector:就比Arraylist多了个 synchronized (线程安全),因为效率较低,现在已经不太建议使用。 hashTable:就比hashMap多了个synchronized (线程安全),不建议使用。 ConcurrentHashMap:是Java5中支持高并发、高吞吐量的线程安全HashMap实现。它由Segment数组结构和HashEntry数组结构组成。Segment数组在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键-值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构;一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素;每个Segment守护着一个HashEntry数组里的元 素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。(推荐使用) 9. Java集合的快速失败机制 “fail-fast”?10. 怎么确保一个集合不能被修改?可以使用 Collections. unmodififiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。 示例代码如下: List list = new ArrayList(); list. add("x"); Collection clist = Collections. unmodifiableCollection(list); clist. add("y"); // 运行时此行报错 System. out. println(list. size());

二、Collection接口

List接口11. 迭代器 Iterator 是什么?12. Iterator 怎么使用?有什么特点?

Iterator 使用代码如下:

List list = new ArrayList(); Iterator it = list. iterator(); while(it. hasNext()){ String obj = it. next(); System. out. println(obj); }

Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModifificationException 异常。

13. 如何边遍历边移除 Collection 中的元素?14. Iterator 和 ListIterator 有什么区别?Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。 Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。 ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。 15. 遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么?16. 说一下 ArrayList 的优缺点17. 如何实现数组和 List 之间的转换?数组转 List:使用 Arrays. asList(array) 进行转换。 List 转数组:使用 List 自带的 toArray() 方法。 代码示例: // list to array List list = new ArrayList(); list.add("123"); list.add("456"); list.toArray(); // array to list String[] array = new String[]{"123","456"}; Arrays.asList(array);18. ArrayList 和 LinkedList 的区别是什么?数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为ArrayList 增删操作要影响数组内的其他数据的下标。 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。 线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全; 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。 LinkedList 的双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。19. ArrayList 和 Vector 的区别是什么?20. 插入数据时,ArrayList、LinkedList、Vector谁速度较快?阐述 ArrayList、Vector、LinkedList 的存储性能和特性?ArrayList和Vector 底层的实现都是使用数组方式存储数据。数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。 Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较ArrayList差。 LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,所以 LinkedList 插入速度较快。 21. 多线程场景下如何使用 ArrayList?ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样: List synchronizedList = Collections.synchronizedList(list); synchronizedList.add("aaa"); synchronizedList.add("bbb"); for (int i = 0; i < synchronizedList.size(); i++) { System.out.println(synchronizedList.get(i)); }22. 为什么 ArrayList 的 elementData 加上 transient 修饰?ArrayList 中的数组定义如下:

private transient Object[] elementData;

再看一下 ArrayList 的定义: public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。transient 的作用是说不希望 elementData 数组被序列化,重写了 writeObject 实现: private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ *// Write out element count, and any hidden stuff* int expectedModCount = modCount; s.defaultWriteObject(); *// Write out array length* s.writeint(elementData.length); *// Write out all elements in the proper order.* for (int i=0; i>> 16); } //实现Map.put和相关方法 final V putVal(int hash, K key, V value, Boolean onlyIfAbsent, Boolean evict) { Node[] tab; Node p; int n, i; // 步骤①:tab为空则创建 // table未初始化或者长度为0,进行扩容 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 步骤②:计算index,并对null做处理 // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数 组中) if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); // 桶中已经存在元素 else { Node e; K k; // 步骤③:节点key存在,直接覆盖value // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // 将第一个元素赋值给e,用e来记录 e = p; // 步骤④:判断该链为红黑树 // hash值不相等,即key不相等;为红黑树结点 // 如果当前元素类型为TreeNode,表示为红黑树,putTreeVal返回待存放的node, e可能为null else if (p instanceof TreeNode) // 放入树中 e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); // 步骤⑤:该链为链表 // 为链表结点 else { // 在链表最末插入结点 for (int binCount = 0; ; ++binCount) { // 到达链表的尾部 //判断该链表尾部指针是不是空的 if ((e = p.next) == null) { // 在尾部插入新结点 p.next = newNode(hash, key, value, null); //判断链表的长度是否达到转化红黑树的临界值,临界值为8 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st //链表结构转树形结构 treeifyBin(tab, hash); // 跳出循环 break; } // 判断链表中结点的key值与插入的元素的key值是否相等 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) // 相等,跳出循环 break; // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表 p = e; } } //判断当前的key已经存在的情况下,再来一个相同的hash值、key值时,返回新来的value这个 值 if (e != null) { // 记录e的value V oldValue = e.value; // onlyIfAbsent为false或者旧值为null if (!onlyIfAbsent || oldValue == null) //用新值替换旧值 e.value = value; // 访问后回调 afterNodeAccess(e); // 返回旧值 return oldValue; } } // 结构性修改 ++modCount; // 步骤⑥:超过最大容量就扩容 // 实际大小大于阈值则扩容 if (++size > threshold) resize(); // 插入后回调 afterNodeInsertion(evict); return null; }判断键值对数组tablei是否为空或为null,否则执行resize()进行扩容; 根据键值key计算hash值得到插入的数组索引i,如果tablei==null,直接新建节点添加,转向⑥,如果tablei不为空,转向③; 判断tablei的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; 判断tablei 是否为treeNode,即tablei 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向5; 遍历tablei,判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; 插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。33. HashMap的扩容操作是怎么实现的?final Node[] resize() { Node[] oldTab = table; //oldTab指向hash桶数组 int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { //如果oldCap不为空的话,就是hash桶数组不为空 if (oldCap >= MAXIMUM_CAPACITY) { //如果大于最大容量了,就赋值为整数最大的阀值 threshold = Integer.MAX_VALUE; return oldTab; //返回 } //如果当前hash桶数组的长度在扩容后仍然小于最大容量 并且oldCap大于默认值16 else if ((newCap = oldCap = DEFAULT_INITIAL_CAPACITY) newThr = oldThr 0) // initial capacity was placed in threshold newCap = oldThr; // 无参构造创建的map,给出默认容量和threshold 16, 16*0.75 else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } // 新的threshold = 新的cap * 0.75 if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; // 计算出新的数组长度后赋给当前成员变量table @SuppressWarnings({ "rawtypes","unchecked" } ) Node[] newTab = (Node[])new Node[newCap]; //新建hash桶数组 table = newTab; //将新数组的值复制给旧的hash桶数组 // 如果原先的数组没有初始化,那么resize的初始化工作到此结束,否则进入扩容元素重排逻辑,使 其均匀的分散 if (oldTab != null) { // 遍历新数组的所有桶下标 for (int j = 0; j < oldCap; ++j) { Node e; if ((e = oldTab[j]) != null) { // 旧数组的桶下标赋给临时变量e,并且解除旧数组中的引用,否则就数组无 法被GC回收 oldTab[j] = null; // 如果e.next==null,代表桶中就一个元素,不存在链表或者红黑树 if (e.next == null) // 用同样的hash映射算法把该元素加入新的数组 newTab[e.hash & (newCap - 1)] = e; // 如果e是TreeNode并且e.next!=null,那么处理树中元素的重排 else if (e instanceof TreeNode) ((TreeNode)e).split(this, newTab, j, oldCap); // e是链表的头并且e.next!=null,那么处理链表中元素重排 else { // preserve order // loHead,loTail 代表扩容后不用变换下标,见注1 Node loHead = null, loTail = null; // hiHead,hiTail 代表扩容后变换下标,见注1 Node hiHead = null, hiTail = null; Node next; // 遍历链表 do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) // 初始化head指向链表当前元素e,e不一定是链表的 第一个元素,初始化后loHead // 代表下标保持不变的链表的头元素 loHead = e; else // loTail.next指向当前e loTail.next = e; // loTail指向当前的元素e // 初始化后,loTail和loHead指向相同的内存,所以当 loTail.next指向下一个元素时, // 底层数组中的元素的next引用也相应发生变化,造成lowHead.next.next..... // 跟随loTail同步,使得lowHead可以链接到所有属于该链 表的元素。 loTail = e; } else { if (hiTail == null) // 初始化head指向链表当前元素e, 初始化后hiHead 代表下标更改的链表头元素 hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); // 遍历结束, 将tail指向null,并把链表头放入新数组的相应下标, 形成新的映射。 if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }34. HashMap是怎么解决哈希冲突的?答:在解决这个问题之前,我们首先需要知道什么是哈希冲突,而在了解哈希冲突之前我们还要知道什么是哈希才行;35. 能否使用任何类作为 Map 的 key?

可以使用任何类作为 Map 的 key,然而在使用之前,需要考虑以下几点:

如果类重写了 equals() 方法,也应该重写 hashCode() 方法。 类的所有实例需要遵循与 equals() 和 hashCode() 相关的规则。 如果一个类没有使用 equals(),不应该在 hashCode() 中使用它。 用户自定义 Key 类最佳实践是使之为不可变的,这样 hashCode() 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode() 和 equals() 在未来不会改变,这样就会解决与可变相关的问题了。 36. 为什么HashMap中String、Integer这样的包装类适合作为K?37. 如果使用Object作为HashMap的Key,应该怎么办呢?38. HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?39. HashMap 的长度为什么是2的幂次方40. HashMap 与 HashTable 有什么区别?线程安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap ); 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;(如果你要保证线程安全的话就使用 ConcurrentHashMap ); 对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException。 初始容量大小和每次扩充容量大小的不同 : 创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。 推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。 41. 什么是TreeMap 简介TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。 TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。TreeMap是线程非同步的。 42. 如何决定使用 HashMap 还是 TreeMap?对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。 43. HashMap 和 ConcurrentHashMap 的区别ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。) HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。 44. ConcurrentHashMap 和 Hashtable 的区别?

3、JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):

45. ConcurrentHashMap 底层具体实现知道吗?实现原理是什么?如果相应位置的Node不为空,且当前该节点不处于移动状态,则对该节点加synchronized锁,如果该节点的hash不小于0,则遍历链表更新节点或插入新节点; if (fh >= 0) { binCount = 1; for (Node e = f;; ++binCount) { K ek; if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; if (!onlyIfAbsent) e.val = value; break; } Node pred = e; if ((e = e.next) == null) { pred.next = new Node(hash, key, value, null); break; } } }如果该节点是TreeBin类型的节点,说明是红黑树结构,则通过putTreeVal方法往红黑树中插入节点;如果binCount不为0,说明put操作对数据产生了影响,如果当前链表的个数达到8个,则通过treeifyBin方法转化为红黑树,如果oldVal不为空,说明是一次更新操作,没有对元素个数产生影响,则直接返回旧值; 如果插入的是一个新节点,则执行addCount()方法尝试更新元素个数baseCount;四、辅助工具类46. Array 和 ArrayList 有何区别?Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。 Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。 Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList有。47. 如何实现 Array 和 List 之间的转换?Array 转 List: Arrays. asList(array) ; List 转 Array:List 的 toArray() 方法。48. comparable 和 comparator的区别?comparable接口实际上是出自java.lang包,它有一个 compareTo(Object obj)方法用来排序comparator接口实际上是出自 java.util 包,它有一个compare(Object obj1, Object obj2)方法用来排序 一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort(). 49. Collection 和 Collections 有什么区别?java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。 Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。50. TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素?51. Collection 和 Collections 有什么区别?java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。 Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3