集合就像一种容器,可以动态地把多个对象的引用放入容器中。
集合中可以放多种类型的对象。
集合中只能存放对象,不能存放基本类型的数据。如果看到可以直接添加数字,那一定是自动装箱了。
集合分为Collection和Map(注意Map不属于Collection)。
集合的长度是可变的,这点与数组不同。
1.Collection
Collection接口是不按添加顺序存放对象的集合,集合内元素可以重复,即“无序可重复”。Collection接口中有两个常用的子接口List和Set。Collection接口中定义的方法可以操作List和Set。
1.1 List
List接口中存放的元素有序并且可重复,并且其中的每个元素都有都有其对应的索引,就像动态的数组。 List接口中常用的实现类有ArrayList,LinkedList,Vector。
1.1.1 ArrayList
ArrayList底层是用数组实现的。和数组一样,存放元素时是按添加顺序存放的,并且空间是连续的。因此,它遍历和添加元素的效率比较高,但是插入、删除的效率低。
虽然ArrayList是线程不安全的,但是还是推荐使用它。
通常用接口多态来创建一个集合对象:List list = new ArrayList();
向list中添加一个元素:list.add(1); //自动装箱了
得到list的长度:list.size(); //没有length
1.1.2 LinkedList
LinkedList底层是用链表实现的。和链表一样,存放元素时也是按添加顺序存放的,但是空间不是连续的。因此,它虽然遍历和添加元素的效率低,但是插入、删除的效率高。
1.1.3 Vector
Vector比较古老。它和ArrayList没什么大的区别,区别之处就在于它是线程安全的,而且它比ArrayList慢。在使用时应尽量避免使用,一般都会选择ArrayList,即使ArrayList线程不安全,但是可以利用别的办法使线程安全。
1.1.4 测试
List集合中的元素索引是从0开始的,并且是可以存放null的。当删除一个元素时,List集合会自动补全,长度减一。
测试代码:
List list1 = new ArrayList();
list1.add(null);
System.out.println(list1.size());
System.out.println(list1);
list1.remove(0); //0默认为元素的索引而不是元素。
System.out.println(list1.size());
System.out.println(list1);
List list2 = new LinkedList();
list2.add(null);
System.out.println(list2.size());
System.out.println(list2);
list2.remove(0);
System.out.println(list2.size());
System.out.println(list2);
测试结果:
1
[null]
0
[]
1
[null]
0
[]
1.2 Set
Set接口中存放的元素是无序不可重复的,其中没有提供格外的方法,都是继承Collection接口的方法。 Set接口中常用的实现类有HashSet,TreeSet。
Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals 方法。
1.2.1 HashSet
HashSet是Set接口的典型实现类,大多数时候使用Set集合时都使用这个实现类。HashSet不是线程安全的,并且不能保证元素的排列顺序。
HashSet底层是数组实现的,不过是按Hash算法来存储集合中的元素,因此存放在它中的元素不是按照添加顺序存储的。当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
也正是如此,HashSet具有很好的存取和查找性能,因为总是能根据哈希算法很快地找到要寻找的元素并进行操作。
如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
对于存放在Set容器中的对象,对应的类一定要重写equals(Object obj)和hashCode()方法,以实现对象相等规则。
重写 hashCode() 方法的基本原则:
①在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值
②当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等
③对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
1.2.2 LinkedHashSet
LinkedHashSet 是 HashSet 的子类
LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
LinkedHashSet 不允许集合元素重复。
1.2.3 TreeSet
TreeSet底层是用二叉树(红黑树)实现的,存放元素时自动排序,因此检索是效率非常高。
这里需要注意的是,向TreeSet中添加的元素一定是能比较大小的元素,因为TreeSet底层是二叉树,在存放元素时是要排序的。如果一个元素不能比较大小就不能进行排序,也就不能添加到TreeSet集合中。那么问题来了,对象都不能大小,而集合中只能存放对象元素,那这不就矛盾了吗?
事情总是有解决的办法的。既然对象不能比较大小,那么我们就按照对象中的某个属性的大小来决定对象的大小。而事实正是如此。比如要将Student对象放入TreeSet中,我们可以按照Student对象中age属性来决定Student对象的大小。年龄越大的Student对象越大。这时就需要重写Comparable接口的compareTo()方法或Comparator接口的compare()方法来进行对象比较的方法。
注意,TreeSet比较大小不是依据equals()方法或hashCode()方法进行比较的,而是调用compareTo()方法进行比较。
如果用到比较器Comparator,在创建TreeSet时需要将比较器对象当做TreeSet构造器的形参传入:
Set set = new TreeSet(比较器对象);
1.2.4 测试
Set集合中的元素是没有索引的,不过也能存放null。但是,当删除一个元素时,Set集合长度会改变。
测试代码:
Set set1 = new HashSet();
set1.add(null);
System.out.println(set1.size());
System.out.println(set1);
set1.remove(null); //其中的参数不是索引,而是具体元素
System.out.println(set1.size());
System.out.println(set1);
测试结果:
1
[null]
0
[]
测试代码:
Set set2 = new TreeSet();
//set2.add(null); //会报错
set2.add(2);
System.out.println(set2.size());
System.out.println(set2);
set2.remove(2); //这个1不是索引,而是元素本身
System.out.println(set2.size());
//String a = "你好"; //字符串因为无法比较,所以添加时会报错
//String b = "好你";
//set2.add(a);
//set2.add(b);
System.out.println(set2);
测试结果:
1
[2]
0
[]
2. Map
Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 key 用 Set 来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法。
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value。
创建一个Map集合:Map map = new HashMap();
向Map集合中添加一个元素:map.put(Object key, Object value);
从一个Map集合中得到一个元素:map.get(key);
删除Map中的一个元素:map.remove(key);
得到Map结合中的所有Key: Set keys = map.keySet();
Map接口的常用实现类:HashMap、 TreeMap 和 Properties。
2.1 HashMap
HashMap是 Map 接口使用频率最高的实现类。
允许使用null键和null值,与HashSet一样,不保证映射的顺序。
HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
2.2 TreeMap
TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。
TreeMap 的 Key 的排序:
①自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException。
②定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口。
TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。
若使用自定义类作为TreeMap的key,所属类需要重写equals()和hashCode()方法,且equals()方法返回true时,compareTo()方法应返回0。
2.3 Properties
Properties类是HashTable类的子类,用于处理属性文件。
在使用时需要在当前工程下简历xxx.properties配置文件,文件中的内容都是键值对,中间用=隔开。
由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型。
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法。
在程序中使用:
Properties properties = new Properties();
properties.load(new FileInputStream("xxx.properties")); //加载properties文件
String user = properties.get("key");
注意,在xxx.properties文件中修改配置属性时,不用重启程序,会自动加载。
Hashtable是个古老的 Map 实现类,线程安全。
与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value。
与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序。
Hashtable判断两个key相等、两个value相等的标准,与hashMap一致。
3. 遍历集合
3.1 foreach
和遍历数组一样。
3.2 Iterator接口
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。
Iterator接口中有两个常用方法:
hasNext()方法:询问集合中有无下一个元素,迭代器指针不移动。
next()方法:得到当前元素,指针移到下一个元素。
在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。
使用如下:
Iterator iterator = coll.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
3.3 Map集合的遍历
Map集合的遍历比较特殊,需要通过遍历Map对象的键的集合来遍历。
Set keys = map.keySet();
for(Object key : keys){
map.get(key);
}
4. Collections类
和数组有Arrays工具类一样,集合也有Collections工具类,其中的方法也都是静态方法,因此调用时不需要创建对象,直接Collections.方法名();即可。
by Karl