赞
踩
一、集合框架体系
集合的理解与好处
-数组扩容麻烦,保存的数据往往为同一类型
-集合可以动态保存任何多个对象,使用方便
-集合提供了一系列方便的操作对象和方法:add、remove、set、get等
Java的集合类很多 主要分为Collection单列集合 和Map双列集合(键值对)
Collection 是一个接口 继承了Iterable接口
Collection下有两个重要接口 – List -Set 其中有许多重要的实现方法
Collection 实现类的特点
Collection接口遍历元素方式1-使用Iterator迭代器
-Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
-所有实现了Collection接口的集合类都有一个iterator()方法(这个方法是由Collection父接口Iterable而来),用于返回一个实现了Iterator接口的对象,即可以返回一个迭代器
-Iterator结构:
-Iterator仅用于遍历集合,本身并不存放对象
迭代器执行原理
调用next()方法时,必须先调用hasNext()判断,不然会抛异常
迭代器案例:(book是一个自写类)
这里iterator的while循环可以用快捷键itit自动生成
遍历完成后的iterator迭代器指向最后一个元素
使用语句iterator = col.iterator()可以将指针再次置于集合首部
Collection接口遍历元素方式2-使用增强for循环
-增强for循环其实就是简化版的迭代器,本质一样,可以代替迭代器,但只能用于遍历Collection集合或数组
-基本语法为:for(元素类型 元素名 :集合名或数组名){ 访问元素 }
-增强for循环的底层仍然是迭代器(会直接调用iterator()),用它主要是它写起来比迭代器简便
增强for循环案例:
Map是另一种重要的集合接口 其中也有几种重要的实现方法
具体内容在下面Map环节介绍
二、Collection – List
1、List接口
List接口基本介绍
-List集合类中的元素有序(即太你家顺序和取出顺序一致)、且可重复
-List集合中每个元素都有其对应的顺序索引,即支持索引
-List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
-JDK API中List中有许多实现类,常用的有ArrayList LinkedList Vector
List接口的常用方法
-void add(int index , Object ele)在index索引插入元素
-boolean addAll(int index , Collection eles)从index位置将所有eles中的所有元素添加进来
-Object get(int index):获取指定index位置的元素
-int indexOf(Object obj) 返回obj在集合中首次出现的位置
-int lastIndexOf(Object obj) 返回obj在集合中末次出现的位置
-Object remove(int index)一处指定index位置的元素,返回此元素
-Object set(int index , Object ele) 将index处的元素替换为ele
-List subList(int fromIndex , int toIndex)返回fromIndex到toIndex位置的子集合
List的遍历方式除了上面提到的迭代器和增强for循环,还有一种普通for循环,因为list接口里的实现类底层都是数组,可以这么遍历
2、ArrayList
ArrayList的一些小知识
-ArrayList可以放空值也就是null,并且可以加入多个
-ArrayList是由数组实现数据存储
-ArrayList基本等同于Vector,只是ArrayList是线程不安全的,但ArrayList执行效率高,在多线程情况下不建议使用ArrayList
ArrayList底层结构与源码
-ArrayList中维护了一个Object类型的数组
-当创建ArrayList对象时,如果使用的是无参构造器,则初始数组的容量为0,第一次添加则扩容为10,如果需要再次扩容,则扩容后的数组容量为1.5倍
-如果构造指定参数容量,则扩容时为原容量的1.5倍
如果向了解更多,可以去查看ArrayList的底层源码
3、Vector
Vector基本介绍:
--Vector是在JDK1.0时出现 , 而ArrayList是在JDK1.2
-Vector的操作与ArrayList基本一致
-Vector的底层也是一个Object类型的数组
-Vector是线程同步的,也就是线程安全的,Vector类的操作方法带有synchronized
-由于加了线程锁,Vector的效率不如ArrayList
-初始化与扩容机制类似ArrayList,无参构造的初始容量为10,不过每次扩容都是2倍扩
Ps(底层源码中有个capacityIncrement变量定义扩容大小,默认为0,如果使用者不指定,就默认扩容为oldCapacity,也就是两倍扩)
扩容的核心源码:
4、LinkedList
LinkedList的介绍:
-LinkedList底层实现了双向链表和双端队列特点
-可以添加任意重复元素,如null
-线程不安全,没有实现线程同步
-LinkedList中维护了两个属性first 和last分别指向首节点和尾节点,还有一个size属性记录长度
-每个节点(Node对象)里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,在雨中实现双向链表
-使用LinkedList元素的添加与删除不是通过数组完成,效率较高
-linkedlist.remove()默认删除第一个
底层源码运行刨析:
再添加一个元素时:
List.remove()的核心源码:
先走removeFirst:
再走unlinkFirst将f指向的双向链表的第一个节点拿掉
讨论ArrayList和LinkedList比较
-ArrayList底层结构是可变数组,增删的效率低,改查的效率高(数组特性)
-LinkedList底层结构是双向链表,增删效率高,改查效率低
-一般来说,在程序中80-90%都是查询,大部分情况用ArrayList
三、Collection – Set
1.Set接口
Set基本介绍:
-添加到里面的元素无序,没有索引
-取出的顺序是固定的,不会每次取的时候顺序都不一样
-不允许重复元素,所以最多包含一个null
-JDK中实现Set的类有:HashSet LinkedSet TreeSet 等等
-和List接口一样,Set接口也是Collection的子接口,因此常用方法和Collection一样
Set接口的遍历方式:
-可以使用迭代器
-可以使用增强for循环
-不能使用索引的方式获取
2.HashSet
HashSet介绍:
-HashSet实现了Set接口
-HashSet的底层实际上是HashMap
-可以存放null值,但只可存放一个
-HashSet不保证元素有序,取决于hash后再确定索引结果
-不能有重复对象
HashSet注意的点:
-HashSet在执行add方法时返回一个布尔值,代表是否添加成功
-在HashSet里面加入属性一样的其它类的对象时是可以的,因为这两个对象不算一个对象
-在HashSet里面加入两次一样的String对象是不行的这里取决于源码
这是一道经典面试题
HashSet底层说明,分析HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)(拉链法解决Hash冲突)
如上图所示数组+链表(当链表的容量大于一个值时,JDK会将其变成一个红黑树) 存储效率会非常高
源码说明:
-HashSet底层是HashMap
-添加一个元素时,先得到hash值,会转成索引值
-找到存储数据表table,看这个索引位置是否已经存放有元素
-如果没有,直接加入
-如果有,调用equals进行比较,如果相同,就放弃添加,如果不同,则进行table位置元素链表的下一位进行比较,一直比较到最后一位,都不同就添加到链表末尾(前面问题的核心就在这里,如果是相同的字符串,equals会认为它相同,而属性相同的类的equals不会相同)
-在JDK8中如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8)并且table的大小超过MIN_TREEIFY_CAPACITY(默认64),就会进行红黑树化,如果table的大小没有超过这个值,就先扩table表,默认是按两倍。
根据putval()函数分析,如果这个put方法返回的null,则代表元素插入成功,如果不是空,就代表应放入的位置已经存在其它元素,返回会是这个元素。
这里去看hash函数得到的hash值可以发现,hash值明显不等于hashCode(),而是hashCode()值的一个右移16位异或运算
执行putval(重要)
table是放节点的数组
当table一开始容量为空时,容量会被初始化为16,当用了12个空间就会开始扩容resize()方法(会有0.75的缓冲加载因子,也可以说是4个空间的缓冲,因为怕突然有很多元素添加)扩容后table容量变为32,则扩容临界值就会变为24,依次类推
当table一开始容量为空时,容量会被初始化为16,当用了12个空间就会开始扩容resize()方法(会有0.75的缓冲加载因子,也可以说是4个空间的缓冲,因为怕突然有很多元素添加)扩容后table容量变为32,则扩容临界值就会变为24,依次类推
putVal()源码注释详解
- final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
- boolean evict) {
- Node<K,V>[] tab; Node<K,V> p; int n, i;
- //当加入第一个元素时,map中并没有key-value,此时table为null
- //此时就将table初始化为容量为16的Node[]数组
- if ((tab = table) == null || (n = tab.length) == 0)
- //使用resize函数生成tab[]数组
- n = (tab = resize()).length;
- //(n - 1) & hash 相当于对将hash % n,根据hash得到table的索引
- //根据key计算得到的hash值,计算该key应该存放到table表的那个索引位置,
- //并且把这个位置的对象赋给p
- //再判断p是否为null
- //如果p为空,表示还没有存放元素,就创建一个Node(key = 创建的 ,value = PRESENT)
- //就放在该位置 tab[i] = newNode(hash, key, value, null);null代表后面还没有链其它元素 ,hash用于继续跟以后相同位置的节点比较
- //这里的p指向当前table位置的元素
- if ((p = tab[i = (n - 1) & hash]) == null)
- //当table内对应位置没有元素的时候,实例化一个元素作为该位置的第一个元素
- tab[i] = newNode(hash, key, value, null);
- else {//当table内有元素时候,就需要解决hash冲突问题了
- Node<K,V> e; K k;
- //table内的第一个元素的hash值与新加入key-value的hash相同的时候并且满足(加入的key与p指向的Node节点的key一样时)或(p指向的Node节点的key的equals()和准备加入的key执行比较后相同),就表示不能往里面添加了
- if (p.hash == hash &&
- ((k = p.key) == key || (key != null && key.equals(k))))
- e = p;
- //再判断p是不是一棵红黑树,如果是,就用红黑树进行比较
- else if (p instanceof TreeNode)
- //如果此时table内已经树化,使用putTreeVal方法加入元素,若存在相同的key的元素,则将引用返回
- e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
- else {
- //如果table内是链表,则对链表后每一个元素进行比较,都不同就插入链表,这里使用尾插法
- //再把元素添加到链表后,立即判断该链表是否有8个节点,如果达到就调用treeifyBin()对当前这个链表进行树化转成红黑树,在进行树化时,还进行一个判断,如果table容量小于64,则会先对table进行扩容,这个判断扩容机制包含在treeifyBin()函数中
- for (int binCount = 0; ; ++binCount) {
- if ((e = p.next) == null) {
- p.next = newNode(hash, key, value, null);
- if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
- //bitCount大于树化的阈值,转化为红黑树
- treeifyBin(tab, hash);
- break;
- }
- //table内存在元素的key值与新加入key-value的key相同的时候,用e指向p(仅仅指向)
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k)));
- break;
- p = e;
- }
- }
- //通过上面的code,若map中已经存在相同的key,
- //我们则将Node<K,V> e指向该key-value,即Node(TreeNode是Node的子类)
- //若e == null,则说明已经插入成功
- //若e != null, 则将e.value作为旧值返回,将e.value设置为value
- if (e != null) { // existing mapping for key
- V oldValue = e.value;
- //putVal中的参数。若onlyIfAbsent为null或者oldValue为空时才替换,
- if (!onlyIfAbsent || oldValue == null)
- e.value = value;
- afterNodeAccess(e);
- return oldValue;
- }
- }
- ++modCount;//代表插入次数
- //插入后大于阈值,使用resize()进行调整(一开始为12,也就是)
- if (++size > threshold)
- resize();
- afterNodeInsertion(evict);//对于HashMap这是一个空方法,是给LinkedHashMap的等其它子类实现的方法,比如实现链表有序等等
- return null;
- }
总结:
第一次添加HashSet添加元素:
-创建HashMap ,执行add方法,执行put方法,执行putval方法
-在putval方法内,创建一个table,初始化容量为16,计算元素hash值
-在table对应位置添加新节点,保存hash,key,value,null值,hash用于比对,null代表后面没有链上其它元素
-modCount++,表示插入次数+1,size++表示容量用了一个
-判断size是否大于12(16*0.75),否,不执行扩容
-put和add返回空,表示插入成功。
第二次添加元素(不与第一次冲突):流程与第一次基本一样,插入后modCount++,size++
第三次添加元素(与第一次冲突):
-前几步与第一次一样,当进行到putval()函数时,新建一个Node对象存放需要存放的键值对,计算其hash值。
-p指向hash值对应的table位置本来含有的元素,利用p对新加入的元素进行比对
-通过equals函数得到两个字符串相同,则直接跳出,并将字符串作为返回值返回给put与add方法
-代表加入失败
….
第n次加入元素:会涉及到链表添加元素,和链表红黑树化以及table扩容问题,这里的红黑树化是用方法replacementTreeNode对指针p进行树化,已经细节已经在代码注释中给出。
总的来说,Java的哈希这一套机制是拉链法解决hash冲突典型代表。
前面的HashSet相同字符串不会添加,而相同属性的两个对象会添加的原因就在于比对过程中的equals方法,在String类中重写的equals方法比对内容相同的字符串会去比对两个字符串在jvm中的内存地址是否相同,HashSet在添加String类型的元素时就调用String中重写的equals方法,所以就会导致String类型的重复元素无法添加,自定义类的对象的可以,所以在使用HashSet添加自定义类型的对象时就应该重写equals方法和HashCode()方法,(关于这里为什么要同时重写equals和HashCode()方法,在我发布的之前的博文中有详细介绍。)
重写HashCode()和equals方法一般要写在要放入HashSet的类中
这里给出一个例子:
-写一个Employee类,有属性name和age重写equals()和HashCode()方法使这个类只要在name和age相同时就返回一样的HashCode
- import java.util.HashSet;
- import java.util.Objects;
-
- public class MyHashSet {
- public static void main(String[] args) {
- HashSet hashSet = new HashSet();
-
- hashSet.add(new Employee("孙悟空",500));
- hashSet.add(new Employee("唐僧",1000));
- hashSet.add(new Employee("孙悟空",500));
-
- System.out.println("HashSet表:"+hashSet);
-
- }
-
- }
-
- class Employee{
- private String name;
- private int age;
-
- public Employee(String name , int age){
- this.name = name;
- this.age = age;
- }
-
- public String getName() {
- return name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "Employee{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
-
- //如果name和age的值相同则返回相同的HashCode()
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Employee employee = (Employee) o;
- return age == employee.age && Objects.equals(name, employee.name);
- }
-
- @Override
- public int hashCode() {//HashCode取决于名字和年龄
- return Objects.hash(name, age);
- }
- }
运行结果:
可以见到:孙悟空这个对象并没有添加两个,说明重写的equals方法和HashCode方法有效
在重写类的equals和HashCode对象时一般都用快捷键Alt+insert,选中equals和hashCode
表示用java8之后的写法
表示只要name和age一样就返回一样的hashCode
表示只要name和age一样,equals方法就认为一样
如果追进hashCode方法:
这里是一套成熟的数学公式,主要是防止不同对象的碰撞,这里不做过多讨论
最后注意:这一切的HashSet集合的动作都是建立在Java8的基础之上
3.LinkedHashSet
LinkedHashSet介绍:
-LinkedHashSet是HashSet的子类
-LinkedHashSet底层是一个LinkedHashMap,维护了一个数组+双向链表
-LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使链表维护元素的次序,这使得元素看起来是以插入顺序保存的
-LinkedHashSet不允许添加重复元素
底层机制:
-LinkedHashSet底层维护的一个LinkedHashMap(是HashMap子类)
-LinkedHashMap底层是一个table表和双向链表保证元素的加入与取出顺序一致
-添加第一次时,直接将数组table扩容到16,存放的节点类型是LinkedHashMap$Entry
-数组是HashMap$Node[]存放是元素/数据是LinkedHashMap$Entry,说明这里后者肯定是前者的一个子类,这里就是数组多态的一个体现,子类对象可以存放到父类数组里面去
-Entry里面才真正存放了before/after属性,也就是链表双向属性,Entry对HashMap的继承关系在内部类完成,是一个静态内部类,因为可以直接通过HashMap.Node访问。
这里内部类的各种知识可参照:
-在添加一样的元素时,进入到与之前HashSet一样的HashMap里面的add和put方法里面去然后进入到putval方法,与之前一样
LinkedHashSet想让相同属性的自写类不能加入与之前的HashSet一样,步骤完全一样
可以看到,这里取出的顺序与加入的顺序完全一致
4.TreeSet
TreeSet介绍:
-当我们使用无参构造器创建TreeSet时,仍然是无序的
-使用TreeSet的一个构造器,可以传入一个比较器Comperator(匿名内部类),指定排序规则。
例子:
源码解读:
-构造器把传入的比较器对象,赋给了TreeMap的Comparator属性
-添加元素调用TreeSet.add方法会执行到cpr,cpr就是一个匿名内部类的对象,最后使用的compare方法就是我们在定义TreeSet时传入的compare方法
-当传入TreeSet一样的对象时,compare函数会返回0,key会覆盖,也就是相同对象不能加入TreeMap.
上一个例子如果按字符串长度来排序key值,compare函数的写法:
这里如果把比较方法改成这样,那“abc”也无法加入,因为abc的长度为3与“tom”一致,会导致compare方法返回0而无法加入TreeMap
四、Map
1.Map接口
Ps:Map的元素是由键值对构成,前面提到的HashSet底层也是Map元素,但不是键值对,是因为在使用HashSet时只只用了它的key,而value被一个静态常量present所代替
Map(JDK8)介绍:
-Map与Collection并列存在,用于保存具有映射关系的数据:key-value
-Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
-Map中的key不允许重复,原因和HashSet一样
-Map中的value可以重复
-Map中的key可以为null,value也可以为null,key只能有一个,但value可以有多个null
-Map中如果添加了key相同value不相同的元素,则会用后添加的value替换之前的value
-常用String类型作为Map的key比较多
-key和value之间存在单一映射一对一关系,即通过key总能找到对应的value
-Map存放数据的key-value是放在一个Node中,因为Node实现了Entry接口,有些书上说一对key-value就是一个Entry(这不严谨)
难点:有些书上说key是存放在一个set中,value是存放在一个Collection中,实际上其实key和value还是存放在HashMap$Node中,set中存放的是key的引用,Collection中存放的也是value的引用。这里是为了使用者方便地遍历value和key数据,java提供了一个set和一个Collection把其引用做成key-value的引用做成一组对象放在Entry里面,再将Entry放在EntrySet集合里面。
简易来说就是:
-k-v最后是由语句HashMap$Node node = newNode(hash , key , value , null)创建与存储
-k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素类型是Entry,而一个Entry对象就有k-v 也就是EntrySet<Entry<K,V>>;
从源码来分析这个问题:
这里Entry存放在一个set中set的类型是EntrySet中,而EntrySet又是HashMap的一个内部类
在entrySet定义的类型是Map.Entry,但实际上存放的还是由HashMap$Node类型转成的Entry
这里我们获取set的类型是HashMap$EntrySet , 获取entry的类型却是HashMap$Node
为什么可以这样做:因为源码中HashMap$Node实现了Entry接口:
从table表里取出的是HashMap$Node类型
从entrySet集合里取出的是HashMapEntry类型:
这里两者一一对应,且如果从地址角度来看,是由entrySet里面的元素指向table里面的元素,并没有创造一个新的数据:
做以上这些操作的根本原因还是因为java设计者认为把HashMap$Node对象存放到entrySet就方便我们遍历,因为Map.Entry提供了getKey()和getValue(),可以让我们单独地取出table里的key对象
从HashMap$Node中分别取出k和v:
这里分别取出的key和value的运行类型分别是HashMap$KeySet和HashMap$Values都是HashMap的内部类(它们又是继承是Set和Collection的抽象类)
Map接口常用方法:
-put:添加
-remove:根据键来删除映射
-get:根据键来获得其值
-size:获取元素个数
-isEmpty:判断个数是否为0
-clear:清除
-containsKey:查找键是否存在
运行结果无序
Map的遍历方式:(重要方法:
-keySet:获取所有的键
-entrySet:获取所有关系k-v
-values:获取所有的值)
)
1、先取出所有的key,通过key取出所有的value
-增强for循环:
;
-迭代器:
2、直接取出所有的value
-增强for循环:
-迭代器
3、通过EntrySet来获取k-v
-增强for循环:
这里从entrySet里取出来的是HashCode$Node类型,将其强转成Entry再使用getKey和getValue。
-迭代器
2.HashMap
HashMap介绍:
-Map接口常用实现类:HashMap Hashtable Properties
-HashMap是Map接口使用频率最高的实现类
-HashMap是以k-v对来存储数据(HashMap$Node类型)
-key不能重复,value可以,可以是null
-如果添加相同的key,会覆盖原来的value,等同于替换key的value(具体怎么替换,可以参考之前HashSet里面提到的源码putVal方法)
-不保证映射顺序,因为底层是hash表的方式存储
-HashMap没有实现同步,因此线程不安全
-底层是数组+链表+红黑树,具体细节看之前的源码解读
由于HashSet底层是HashMap,所以这里HashMap特性很多上篇都有:
-HashMap底层维护了Node类型的数组table,默认为null
-当创建对象时,加载因子(laodfactor)初始化为0.75
-当添加key-val时,通过key的哈希值得到table的索引。然后判断改索引处是否又元素,没有元素直接添加,有元素则继续判断该元素的key和准备加入的key是否相等,如果相等,则直接替换val,如果不相等则判断是树结构还是链表结构,做出相应处理,如果添加时发现容量不够,则需扩容
-第一次添加,初始化table的容量为16,临界扩容值为12
-以后再次扩容,则需要扩容table容量为之前的2倍,临界值依然为最大值乘以加载因子
-在java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认为8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认为64),就会进行树化(红黑树)
3.Hashtable
Hashtable介绍:
-存放的元素是键值对:k-v
-hashTable使用的方法基本和HashMap一样
-hashTable是线程安全的
-继承了dictionary类,实现了Map接口
-hashTable的键与值都不能放null,会抛空指针异常
-与HashMap一样,加入key一样的元素时,会替换其value
Hashtable底层结构源码:
-底层有数组Hashtable$Entry[],初始化为11
-有一个临界值threshold 8 = 11*0.75,table内元素大于此值会进行扩容(默认不是两倍扩容,有其自己的扩容机制[乘以2+1]),第一次扩容后容量会变成23,临界值变成17
-方法addEntry(hash , key , value , index);添加k-v封装到Entry
底层put方法有synchronized锁,线程安全
不允许null的加入:
扩容机制:
与HashMap的一点区别:
4. ConcurrentHashMap
ConcurrentHashMap与HashMap、Hashtable对比:
-在并发环境下,HashMap线程不安全,可能会形成环状链表(扩容时造成)会导致get操作时cpu空转,在并发环境中使用是非常危险的
-Hashtable与HashMap几乎一样,但Hashtable不允许键值为null
-Hashtable是线程安全的,但实现代价比较大,get/put所有相关方法都是synchronized的,这相当于给整个哈希表加了一把大锁,多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞。
-ConcurrentHashMap是线程安全的,每把锁只锁一段数据,也就是分段锁,当不同线程访问不同数据时,不会存在锁的竞争,对比Hashtable效率有不小的提升
ConcurreHashMap更多源码与细致思想,详见博客:
5.LinkedHashMap
参考之前的LinkedHashSet,因为LinkedHashSet底层就是LinkedHashMap,底层是数组+双向链表保证插入与取出的顺序一致
6.TreeMap
前面提到TreeSet的底层就是TreeMap,TreeSet初始化时会将Map的value初始化成一个静态不变参数present,所以TreeMap的许多特性与TreeSet基本一致
TreeMap简易介绍:
-用默认构造器创建TreeMap时,元素无序
-TreeMap构造器提供了一个Comparator的匿名内部类,可以重写compare方法来自定义key值排序
按key(字符串)字母顺序从小到大排序例子:
按字符串长度大小从小到大进行排序:
源码内容与之前的TreeSet一样。
7.Properties
Properties介绍:
-Properties类继承自Hashtable类并且实现了Map接口,也是一种键值对的形式来保存数据
-使用特点与Hashtable类似
-Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
-xxx.properties文件通常作为配置文件
-Porpreties也不能添加null
-也是经过hash存表,也是无序
具体使用方法在我之前的JavaI/O流中有详细案例
五、Collections
Collections工具类的介绍:
-Collections是一个操作Set、List、Map等集合的工具类
-Collections中提供了一系列的静态方法对集合元素进行排序、查询和修改操作
常用方法(均为static方法):
-reverse(List)反转List中的元素
-shuffle(List)对List集合中的元素进行随机排序
-sort(List)根据元素的自然顺序对指定List集合元素按升序排序
-sort(List , Comparator)根据指定的Comparator长生的顺序对List集合元素进行排序
-swap(List , int , int )将List中i,j位置的元素进行交换
-Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
-Object max(Collection , Comparator)根据Comparator指定的顺序,返回最大元素
-Object min(Collection)
-Object min(Collection , Comparator)
-int frequency(Collection , Object)返回指定集合中指定元素的出现次数
-void copy(List dest , List src)将src中的内容赋值到dest中
-boolean replaceAll(List list , Object oldVal , Object newVal)使用新值替换List对象所有旧值
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。