1:java集合框架中的接口:
Collection接口:存放的是元素;
Map接口:存放的是键值对;
继承关系:
Collection
├List(有序的,可以重复元素)
│├LinkedLis(get, remove, add 既可以在头部也可以在尾部,所以可以当做stack,queue, deque用; 没有同步方法)
│├ArrayList(不是同步的)
│└Vector(类似于ArrayList, 但是同步的)
│ └Stack
└Set
Map
├Hashtable(同步的)
├HashMap(非同步的)
└WeakHashMap(如果一个key不再被外部所引用,那么该key就可以被GC回收。)
Map通过initial capacity和load factor两个参数调整性能。
作为key的对象必须实现hashcode和equals方法。
List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是不能集合里元素不允许重复的原因)。
Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value。
2 为什么集合类没有实现Cloneable和Serializable接口?
cloneable其实就是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功,如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。
首先说明Serializable主要作用将类的实例持久化保存,序列化就是保存,反序列化就是读取。保存也不一定保存在本地,也可以保存到远方。类一定要实现Serializable才可
应该集合中的元素自己实现,定义自己的行为。
3 如何判断一个类是否继承实现另外一个类。
boolean result = Serializable.class.isAssignableFrom(String.class);
4 什么是迭代器(Iterator)?
它可以使得对于序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的。只要拿到这个对象,使用迭代器就可以遍历这个对象的内部.
主要方法: *.iterator() 得到迭代器, hasNext( ) / next( ) / remove(删除后,指向下一个)
List<String> list = new ArrayList<>(); list.add("a"); Iterator<String> it = list.iterator(); while(it.hasNext()) { System.out.println(it.next()); }
迭代器优点:提供统一的迭代方法;可以在对客户透明的情况下,提供不同的迭代方法;快速失败机制,防止多线程下迭代的不安全操作。
5 快速失败&&安全失败:
(注意:以下的集合的修改是指,集合结构的变化,增加、删除节点,修改某一个节点的对象的内容不算是集合结构的变化。)
快速失败(fail-fast):
在用迭代器遍历一个集合时,遍历过程中,其他线程对集合内容进行修改(增,删等对集合结构方法变化的更改),则
会抛出异常ConCurrentModificationException。迭代器在遍历的时候直接访问集合的元素,并且在遍历过程中使用一modCount变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器用hasNext()/next()遍历一个元素的之前,都会检测modCount,如果改过,就抛出异常。Java.util包中的集合都是快速失败的,不能在多线程下发生并发修改。
安全失败(fail-safe):
如果在迭代器遍历concurrentHashMap的过程中,往map中增/删/改,是不会抛出异常。但是会导致不一致的问题。采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问,而是先复制原集合内容,在拷贝的集合上进行遍历。缺点是:不能访问修改后的内容,
java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改。
安全失败迭代器在迭代中被修改,不会抛出任何异常,因为它是在集合的克隆对象迭代的,所以任何对原集合对象的结构性修改都会被迭代器忽略,但是这类迭代器有一些缺点,其一是它不能保证你迭代时获取的是最新数据,因为迭代器创建之后对集合的任何修改都不会在该迭代器中更新,还有一个缺点就是创建克隆对象在时间和内存上都会增加一些负担。
7 Iterator和ListIterator区别
(简单说: list专用)
Iterator可以用来遍历Set,Map和List,但是ListItrator只能用来遍历List。
Iterator对集合只能向前遍历(hasNext(), Next()).ListIterator是双向都可以遍历。
ListIterator实现了Iterator接口,并包含了其他功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引。
8 HashMap的工作原理
键值对形式存储元素。需要一个hash函数,使用hashCode() 和equals()方法来向集合添加,检索元素。
Put(key, value) : 计算key的hash值,然后把键值对存储在集合中合适对索引上。Entry存储在LinkedList中,所以如果存在entry,它使用equals()方法来检查传递的key是否已经存在,如果存在,它会覆盖value,如果不存在,它会创建一个新的entry然后保存。
Get(key): 首先是用hashcode找到桶,然后key,equals(),判断是否相等。
重要特性是:容量(默认初始值是32),负载因子(默认是0.75),阈值是负载因子乘以容量,如果大于阈值,就会扩容。扩容极限。建议如果在开始的时候知道元素的个数,设定初始值。
容量:桶的数量。
负载因子:有多少桶被占用了。
9 HashMap VS Hashtable
都实现了Map接口,很相似。有如下不同点:
hashMap的键和值是null,hasttable不允许键或值是null.
HashMap不是同步的(适合单线程,是快速失败的),hashtable(适合多线程)是同步的。
HashMap提供了键集合;而Hashtable提供了对键的枚举。
Hashtable是遗留的类。
10 数组(Array)和列表(ArrayList)有什么区别?各自的应用场合?
数组:可以存放基本类型+对象类型;列表:only对象类型(因为LIst里的元素必须继承自object)。
Array大小固定的; ArrayList是动态变化的。
ArrayList提供了很多方法: addAll(), removeall(), iterator()等等。
对于基本类型,集合使用自动装箱来减少代码量;但是,当处理固定大小的基本数据类型的时候,这种方式比较慢。
11 ArrayList VS LinkedList
都是实现了List接口。
ArrayList底层是数组。可以以o(1)时间复杂度对元素进行随机访问。
LinkedList: 是以元素列表的形式存储数据,链表。查找的时间复杂度是0(n),但是插入,添加,删除更快。
LinkedList更费内存,因为要存储指针。
12 Comparable 和 Comparator?
实现了Comparable,表明可以比较元素,可以用SORT排序。Comparator看做是算法的实现,将算法和数据分离。
用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
Comparable接口:只包含了一个compareTo()方法,可以进行排序。
Comparator接口:包含compare()和 equals()两个方法。
二者用法不同:比如在集合中的元素可以自己实现Comparable,这样就可以用Collections.sort()调用进行排序;如果想定义格外的排序方法,可以实现Comparator接口,Collections.sort()另外一个重载方法可以接收一个compareator.
13 java集合类的最佳实践
根据需要正确的选择要使用的。比如:元素大小固定,事先知道,用 Array 而不是ArrayList
对于一些容器(hashMap):如果知道容量,可以提前给一个初始容量,避免扩容,再哈西的性能损耗。
为了类型安全,可读性和健壮性总是要使用泛型,泛型可以避免ClassCastException
用JDK提供的不变类(immutable class)作为Map的键可以避免为我们的类实现hashCode() 和
equals() 方法。
编程的时候接口优于实现。
底层的集合实际上是空的情况下,返回长度是0的集合或数组,不要返回null。
14 java优先级队列(Priority Queue)
每次出队的是优先级最高的。是用大顶堆实现的。要提供一个比较器comparator,否则按照自然顺序排列。
Queue<Test> priQueue = new PriorityQueue<Test>(11,OrderIsdn); // 指定一个comparator
// 默认队列大小是11
Test t1 = new Test("t1",1);
Test t3 = new Test("t3",3);
priQueue.add(t3);
基于优先级堆无界队列,它的元素按照自然顺序排序,在创建的时候可以提供一个比较器。不允许null值。不是线程安全的,入队和出对的时间复杂度是log(n).
15 什么是线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
如何做到线程安全:
四种方式 sychronized关键字
1. sychronized method(){} // 同步方法
2. sychronized (objectReference) {/*block*/} // 同步块
3. static synchronized method(){} // static 同步方法
4. sychronized(classname.class) // 指定同步类
其中1和2是代表锁当前对象,即一个对象就一个锁,3和4代表锁这个类,即这个类的锁。要注意的是sychronized method()不是锁这个函数,而是锁对象,即:如果这个类中有两个方法都是sychronized,那么只要有两个线程共享一个该类的reference,每个调用这两个方法之一,不管是否同一个方法,都会用这个对象锁进行同步。
注意:long 和double是简单类型中两个特殊的咚咚:java读他们要读两次,所以需要同步。
16 如何权衡用无序数组还是有序数组
有序数组查找的时间复杂度是O(logn),但是插入操作的时间复杂度是O(n).
无序的是O(n),但是插入的时间复杂度是O(1).
17 Java中,int 是否是类型安全的。
Int 的赋值是原子的,但是i++不是。
18 HashSet VS TreeSet
HashSet 元素是无序的,最多放入一个NULL值,add(), remove(), contains(), 这些方法的复杂度都是O(1)
TreeSet: 树形结构实现,元素是有序的,不能放入NULL值。 Add(), remove(), contains()复杂度是log(n).
2 hash处理冲突方式?HashMap采用的什么办法?
开放地址法: 线性探测 、 线性补偿法、伪随机数法。
拉链法:HashMap采用链地址法解决冲突:数组+链表到方式。
再散列:
建公共溢出区:
一 ArrayList和LinkedList都实现了List接口,不同点:
(1)ArrayList是基于索引的数据接口(动态数组,和数组不同,数组是new的时候就确认大小了。),它的底层是数组。它可以以O(1)时间复杂度对元素进行随机
访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间 复杂度是 O(n); 看如下的get(i)的源码就能发现这种区别: List<String> array = new ArrayList<>(); get/set都是随机访问,所以ArrayList效率高;
array.set(3, "newvalue");
List<String> linked = new LinkedList<>();
linked.get(4);
linked.set(3, "newvalue");
(2)相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
ArrayList在执行add后,要移动很多元素; remove后也是; 但是Linked不用;(注意,另外一种观点: ArrayList想要在指定位置插入或删除元素时,主要耗 时的是System.arraycopy动作,会移动index后面所有的元素;LinkedList主耗时的是要先通过for循环找到index,然后直接插入或删除。这就导致了两者并非一定谁快谁慢。当数据量较大时,大约在容量的1/10处开始,LinkedList的效率就开始没有ArrayList效率高了,特别到一半以及后半的位置插入时,LinkedList效率明显要低于ArrayList,而且数据量越大,越明显。)
List<String> array = new ArrayList<>();
array.add("aa");
array.add(2, "String");//在指定位置插入元素
array.remove(5);//移除指定位置的元素
List<String> linked = new LinkedList<>();
linked.add("bb");
linked.add(2, "String"); //在指定位置插入元素
linked.remove(5); //移除指定位置的元素
(3)LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
(4) LinkedList是个双向链表,它同样可以被当作栈(一端add/remove)、队列(一端add,另外一端remove)或双端队列(双端add/remove)来使用。
ava中的数据存储方式有两种结构,一种是数组,另一种就是链表,前者的特点是连续空间,寻址迅速,但是在增删元素的时候会有较大幅度的移动,所以数组的特点是查询速度快,增删较慢;而链表由于空间不连续,寻址困难,增删元素只需修改指针,所以链表的特点是查询速度慢、增删快。那么有没有一种数据结构来综合一下数组和链表以便发挥他们各自的优势?答案就是哈希表
HashMap其实就是一个Entry数组,Entry对象中包含了键和值,其中next也是一个Entry对象,它就是用来处理hash冲突的,形成一个链表。
HashMap可以接受null键值和值,而Hashtable则不能;HashMap是非synchronized;HashMap很快.
当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。”这里关键点在于指出,HashMap是在bucket中储存键对象和值对象,作为Map.Entry。
当两个对象的hashcode相同会发生什么? 因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中.
如果两个键的hashcode相同,你如何获取值对象?当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点.
如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。
你了解重新调整HashMap大小存在什么问题吗?当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。这个时候,你可以质问面试官,为什么这么奇怪,要在多线程的环境下使用HashMap呢。
为什么String, Interger这样的wrapper类适合作为键?String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用。因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。其他的wrapper类也有这个特点。不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。如果你可以仅仅通过将某个field声明成final就能保证hashCode是不变的,那么请这么做吧。因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。
我们可以使用自定义的对象作为键吗?当然你可能使用任何对象作为键,只要它遵守了equals()和hashCode()方法的定义规则,并且当对象插入到Map中之后将不会再改变了。如果这个自定义对象时不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了。
我们可以使用CocurrentHashMap来代替Hashtable吗?这是另外一个很热门的面试题,因为ConcurrentHashMap越来越多人用了。我们知道Hashtable是synchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性。
HashMap在JDK1.8及以后的版本中引入了红黑树结构,若桶中链表元素个数大于等于8时,链表转换成树结构;若桶中链表元素个数小于等于6时,树结构还原成链表。因为红黑树的平均查找长度是log(n),长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。还有选择6和8,中间有个差值7可以有效防止链表和树频繁转换。假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。
一 最大的区别就是ConcurrentHashMap是线程安全的,hashMap不是线程安全的
二 ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中。
在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中:
1: 为什么HashMap容量是2的幂?什么是负载因子?
容量:桶的数量。
负载因子:元素个数/容量。
是2的幂的原因: 让元素加入到桶后,分布的更均匀。
2: 为什么String, Integer这样的wrapper类适合作为key?
因为是不可变的,final的,而且重新写了hash()和 equals().、不可变是必要的:为了计算hashcode,防止key的值变化。自己定义的类如果能满足这个要求可以作为map的key。
3:HashMap默认容量是16, 默认装填因子是0.75。
4:HashSet VS TreeSet?
|——SortedSet接口——TreeSet实现类
Set接口— |——HashSet实现类
|——LinkedHashSet实现类
HashSet: 不保证排列顺序/不是同步的/最多能放一个null
当向hashset存入一个元素,HashSet会调用hashCode(),来决定存放位置。
在HashSet中比较两个对象是否相等的方法是:先比较两个对象的hashCode()值是否相等,如果不相等就认为两个对象是不相等的,如果两个对象的hashCode相等就继续调用equals()方法进一步判断两个对象是否相等,如果equals()方法返回true认为两个对象相等,返回false认为两个对象不相等。
TreeSet是一个有序集合,元素中安升序排序,缺省是按照自然顺序进行排序,意味着TreeSet中元素要实现Comparable接口;我们可以构造TreeSet对象时,传递实现了Comparator接口的比较器对象.
HashSet是基于Hash算法实现的,其性能通常优于TreeSet,我们通常都应该使用HashSet.
在我们需要确定是否相等的时候用到HashMap; 排序的功能时,我门才使用TreeSet;
2: java 集合类与数组的区别和联系?
区别:java集合类长度是动态的,数组则是固定的;
java集合类中存的是类对象,数组可以放对象也可以是基本类型;
集合类的各种操作方法更多
array支持多维数组
联系:toArry() 和 Arrays.asList() 二者可以互相转化, 代码如下
String[] strArr = new String[]{"aa", "bb"};
List<String> list1 = Arrays.asList(strArr);
String[] b = (String[]) list1.toArray();
5: 集合类使用最佳实践?
●如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
●如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高;如果多个线程可能同时操作一个类,应该使用同步的类。
●要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
●尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
6:为何Collection不从Cloneable和Serializable接口继承
Collection接口指定一组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,一些如List的Collection实现允许重复的元素,而其它的如Set就不允许。很多Collection实现有一个公有的clone方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为Collection是一个抽象表现。重要的是实现。
当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,具体实现应该决定如何对它进行克隆或序列化,或它是否可以被克隆或序列化。
在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。
7:Map不继承Collection接口?
8:iterator?
Iterator接口提供遍历任何Collection的接口。我们可以从一个Collection中使用迭代器方法来获取迭代器实例。迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者在迭代过程中移除元素。
Iterator接口有三个方法: next, hasNext, remove
9.Enumeration和Iterator接口的区别?
Enumeration的速度是Iterator的两倍,也使用更少的内存。Enumeration是非常基础的,也满足了基础的需要。但是,与Enumeration相比,Iterator更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合。
迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者从集合中移除元素,而Enumeration不能做到。为了使它的功能更加清晰,迭代器方法名已经经过改善。
10.Iterater和ListIterator之间有什么区别?
(1)我们可以使用Iterator来遍历Set和List集合,而ListIterator只能遍历List。
(2)Iterator只可以向前遍历,而LIstIterator可以双向遍历。
(3)ListIterator从Iterator接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
11.遍历一个List有哪些不同的方式?
List<String> list1 = new ArrayList<String>();
for (String str : list1) // 用for-each的方式
{
System.out.print(str);
}
Iterator<String> it = list1.iterator(); // 用迭代器
while(it.hasNext())
{
System.out.print(it.next());
}
Iterator<String> listIt = list1.listIterator(); // 用list的专用迭代器
while(listIt.hasNext())
{
System.out.print(listIt.next());
}
使用迭代器更加线程安全,因为它可以确保,在当前遍历的集合元素被更改的时候,它会抛出ConcurrentModificationException。
12: 什么是fail-fase, 快速失败?
每次我们尝试获取下一个元素的时候,Iterator fail-fast属性检查当前集合结构里的任何改动。如果发现任何改动,它抛出ConcurrentModificationException。Collection中所有Iterator的实现都是按fail-fast来设计的(ConcurrentHashMap和CopyOnWriteArrayList这类并发集合类除外)。
13:hashCode()和equals()方法有何重要性?
HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。当我们试着从HashMap中获取值的时候,这些方法也会被用到。如果这些方法没有被正确地实现,在这种情况下,两个不同Key也许会产生相同的hashCode()和equals()输出,HashMap将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。同样的,所有不允许存储重复数据的集合类都使用hashCode()和equals()去查找重复,所以正确实现它们非常重要。equals()和hashCode()的实现应该遵循以下规则:
(1)如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。
(2)如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。
14:是否可以使用任何类作为hashmap的key?
参考13, hashCode()和 equals()方法要满足规则的不可变类。
15: map可以得到3个集合set 集合
注意这3个集合可以用iterator支持remove操作,但是不支持对集合的add相关操作。
Map<String, String> ma = new HashMap<String, String>();
ma.put("key", "value");
Collection<String> values = ma.values();
Set<String> keyset = ma.keySet();
Set<Entry<String, String>> entrySet = ma.entrySet();
ma.put("key", "value");
Collection<String> values = ma.values();
//values.add("fdsa"); 不可以
Set<String> keyset = ma.keySet();
Iterator<String> it12 = keyset.iterator();
while(it12.hasNext())
{
String fa = it12.next();
it12.remove(); // 对集合的remove操作会影响原来的map, 同样,map的变化也会反映到集合中。 当迭代器遍历的时候,如果map发生变化,会抛出异常。
}
16: 集合的排序
Collections.sort(list<T>, c); // T类型要implements comparable接口。
Collections.sort(list<T>, Comparator<? extend T>); //定义新的comparator函数。
Arrays.sort(); // 对数组进行排序
原文:https://www.cnblogs.com/liufei1983/p/10859193.html