- 关于 List 集合的几个方法测试
- ArrayList 集合特有的方法
- Vector
- LinkedList 集合特有方法
- 泛型
- 集合的泛型作用
- 泛型的要求
- 自定义泛型
- 迭代删除总结
- 集合添加自定义对象——比较是否相等问题
- List 中添加自定义对象,判断是否是同一个对象
- HashSet 和 LinkedHashSet 中添加自定义对象,判断是否是同一个对象
- TreeSet 中添加自定义对象
- List 集合总结
- Set 集合
- 1. HashSet
- 2. LinkedHashSet
- 3. TreeSet
- Map 集合
- 主要方法说明
- LinkedHashMap 和 TreeMap
- HashMap 和 Hashtable
- 总结
关于 List 集合的几个方法测试
每个方法的解释见代码注释
- add()
- containsAll()
- retainAll()
- addAll()
- removeAll()
- Collections.replaceAll()
测试代码
public class CollectionTest {
/**
* add() 方法是定义在 Collection 接口中的.
*
* 在 List 接口中,该方法一直返回true.因为 list 中的元素可以重复,元素的增删通过索引来操作.
*
* 在 Set 接口中,该方法就会有返回 false 的情况,因为 Set 中的元素没有索引,元素不允许有重复.
*/
private static void listMethod() {
List<String> list1 = new ArrayList<String>();
// list1.add("java");
// list1.add("Android");
// list1.add("Java");
// list1.add("工程师");
// list1.add("APP");
List<String> list2 = new ArrayList<>();
list2.add("java");
// list2.add("android");
// list2.add("python");
// list2.add("Android");
// list2.add("Java");
// list2.add("工程师");
// list2.add("APP");
/**
* containsAll()
*
* 判断该集合中是否包含指定的集合
*
* 返回 true 的情况:
*
* 都为空(或者只有参数集合为空)时;
*
* 当给定集合中的元素在该集合中不存在时,返回 false.
*/
// System.out.println(list1.containsAll(list2));
/**
* retainAll() 求交集
*
* 当存在交集(两个集合都为空,也是存在交集的情况)时,返回结果为false, list1会被赋值为交集部分的值
*
* 当参数集合为空或者没有交集时,list1会被赋值为[],返回结果为true
*/
// System.out.println(list1.retainAll(list2));
// System.out.println("list1 retainAll list2后的结果:"+list1.toString());
/**
* addAll()
*
* 当参数集合为空(或者两个集合都为空)时,返回结果为 false.
*
* 先输出 list1 的中的元素,接着输出 list2 中的元素,不会去重.
*/
// System.out.println(list1.addAll(list2));
// System.out.println("list1 addAll list2后的结果:"+list1.toString());
/**
* 当参数集合为空或者参数集合中的元素在 list1 中不存在时,返回 false.
*/
// System.out.println(list1.removeAll(list2));
// System.out.println("list1 removeAll list2后的结果:"+list1.toString());
/**
* replaceAll()
*
* 替换指定集合中的指定元素值
*
* 当给定集合为空或者给定的元素没有找到时,返回 false.
*/
System.out.println(Collections.replaceAll(list1,"java1","java9"));
System.out.println("list1 Collections.replaceAll() 后的结果:"+list1.toString());
}
public static void main(String[] args) {
listMethod();
}
}
ArrayList 集合特有的方法
问题描述
当在遍历一个集合(一种方式通过 for 循环,另一个方式通过 ListIterator 迭代器)时,如果集合中某个元素符合某种条件,那么就对集合的元素进行增加或者其他操作,这个时候不论使用哪种方式进行遍历,当符合条件时,使用 ArrayList.add() 方法添加元素时,那么就出现并发操作异常
ConcurrentModificationException
下面说解决方法。
解决方式代码
/**
* 集合在遍历的同时有修改操作 引起并发操作异常
*
* 解决方案:通过 ListIterator 迭代器(List特有的功能)来操作
*/
private static void listIteratorMethod() {
List<String> list = new ArrayList<>();
list.add("Android");
list.add("java");
list.add("cxs");
list.add("google");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()) {
String string = listIterator.next();
if ("google".equals(string)) {
listIterator.add("google-Android");
}
}
System.out.println(list);
}
ListIterator
是 List 集合特有的方法,可解决并发操作异常。除了这种方式还有另外一种方式,思路如下:
定义一个临时变量,将符合条件的存放在这个变量中,带遍历完后,再将整个集合添加至员集合中。代码如下:
// 定义临时变量解决方案
List<String> tempList = new ArrayList<>();
for (String string : list) {
if ("google".equals(string)) {
tempList.add("Google-Android");
}
}
list.addAll(4, tempList);
System.out.println(list);
Vector
该类始于 JDK1.0版本,从 JDK1.2 版本开始归并于 List 接口下,底层和 ArrayList 一样,通过数组实现。具有如下特点:
- 线程安全的
- 查询快,增删慢
LinkedList 集合特有方法
由于底层通过链表来实现,所以没有办法通过索引直接拿到某个元素,但却提供了直接获取头和尾元素的方法。addFirst(E e)
在该链表的第 0 个位置添加元素。如果多次调用该方法,那么最后添加的在第 0 个位置。addLast(E e)
同样的道理,该方法会在 LinkedList
的最后添加一个元素。
初次之外,还提供了删除头和尾的方法。removeFirst()
该方法会直接移除第 0 个元素。removeLast()
该方法会移除最后一个元素。
以上四个方法有个共同点:返回的结果就是当前操作的元素。
get(int index)
:通过索引来查找某个元素。但是效率低,因为底层是链表结构,源码的实现方式是通过索引和集合的大小除以 2 比较结果决定是从集合的前一半去查找还是后一半去查找。
泛型
集合的泛型作用
- 提高安全性(将运行时期的错误放在编译器)
- 省去了强制类型转换
泛型的要求
- 泛型的类型是引用数据类型
- 集合的前后泛型要一致
- JDK1.7 之后,后面的泛型可不写(称为菱形泛型)
- 如果将泛型的类型定义为 Object ,那么和不写泛型效果是一样的
自定义泛型
- 自定义类:类名后用
<>
包起来,泛型类型的名字可以自己定义,这里用 T 类举例,如
public class CustomGenertic<T>{
}
- 方法定义泛型:方法的泛型最好和该类的泛型一致,当然也可以自定义,如下书写形式:
private<S> void customGenerticMed(S s){
}
这里需要注意如果类中的静态方法定义泛型时,此时的方法泛型定义不能和类的泛型一致(静态方法随着类的加载而加载,有可能在类初始化之前静态方法已经执行,所以静态方法需要有自己的泛型)。
迭代删除总结
- 普通 for 循环
List<String> list = new ArrayList<>();
list.add("Android");
list.add("java");
list.add("cxs");
list.add("google");
list.add("google");
// 普通 for 循环删除需要索引-- (解决连续两个重复元素只能删除一个问题)
for(int i = 0; i < list.size(); i ++) {
if ("google".equals(list.get(i))) {
list.remove(i--);
}
}
System.out.println(list);
- 迭代器方式:可直接删除元素(需要使用迭代器自己的 remove() 方法)
- 增强 for 循环:底层依赖的是迭代器,所以不能直接操作原集合
集合添加自定义对象——比较是否相等问题
List 中添加自定义对象,判断是否是同一个对象
在添加的对象中重写 equals() 方法,判断方式可根据自己的业务来定,一般以某一个或几个属性值是否相等来判断。
HashSet 和 LinkedHashSet 中添加自定义对象,判断是否是同一个对象
在添加的对象中重写 hashCode() 方法和 equals() 方法。系统会首先调用 hashCode() 方法,这个方法里的判断方式可根据自己的业务来定,当此值相等时再调用 equals() 方法来做二次判断,当然,这个方法的判断也可根据自己的业务来定。
TreeSet 中添加自定义对象
- 给对象排序依据
依靠 Comparable 接口中的 compareTo() 方法。如果 TreeSet 集合中添加自定义对象,那么该对象必须实现 Comparable 接口,该方法返回值有三种:
- 返回0:代表二叉树的根元素,如果有元素返回0,则不存,当集合输出时,只会有根元素一个
- 返回负数:如果该元素满足条件,那么会存在左侧,当集合输出时,按照存入的倒序输出。
- 返回正数:如果该元素满足条件,那么会存在右侧,当集合输出时,按照存入的顺序输出。
除了在自定义对象中重写 compareTo() 来做比较规则之外,TreeSet 还提供了一个构造方法通过传入一个比较器对象来对集合中的元素进行排序。
具体的比较规则,可以自己根据业务来定。
- 判断是否是同一对象
也就是保证元素的唯一性,原理同 HashSet 和 LinkedHashSet.
List 集合总结
- ArrayList
- 底层是数组实现的
- 线程不安全
- 查询速度快
- Vector
- 底层是数组实现的
- 线程是安全的
- 查询速度快
- LinkedList
- 底层是链表实现的
- 线程不安全
- 增删速度快,查询慢
Set 集合
1. HashSet
- 特点
- 元素不重复
- 存取无顺序
- 元素没有索引这一说
- 去重原理
依靠底层的 hashCode() 方法和 equals() 方法。为了提高效率,尽量减少对 equals() 方法的调用,也就是尽量在 hashCode() 方法中作出判断的结果。
2. LinkedHashSet
- 特点
- Set 集合中唯一能按照存入顺序取出元素的 Set 集合
- 底层是链表结构
- 元素不重复
- 无索引
3. TreeSet
- 特点
- 会自动给元素排序(默认按照字典顺序)
- 元素不重复
Map 集合
Map 集合底层的算法和 Set 是一样的,Set 集合的 add() 方法底层是通过 Map 集合的 put() 方法来实现的。
Map 集合没有提供可直接遍历的方法,但提供了获取所有键和获取所有值的方法,分别是keySet()
和 values()
,其中 keySet() 方法会返回一个 Set 集合,然后可以利用 Set 集合的迭代器方式遍历所有元素。
主要方法说明
- put(K,V)
返回值的类型是 V,返回的结果代表的是被覆盖的值。如下:
Map<String,Integer> map = new HashMap<>();
map.put("java",50); // 返回 null
map.put("android",55); // 返回 null
Integer v3 = map.put("java",500); // 返回 50
- remove(K)
通过指定的键来删除某个元素。返回值的类型是V,返回值是所给定的 K 对应的值。
Map<String,Integer> map = new HashMap<>();
map.put("java",50);
map.put("android",55);
Integer remove = map.remove("java"); // remove 的值是 50
- get(K)
通过指定键来获取值。返回值即是该键所对应的值。 - values()
返回的是该 Map 集合中所有键对应的值的集合(Collection视图)。 - keySet()
返回的是该 Map 集合中所有键的集合(Set集合)。
遍历 Map 集合
Set<String> keySet = map.keySet();
// 通过迭代器遍历
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key +" == "+map.get(key));
}
System.out.println("--------------------");
// 增强 for 循环遍历
for (String string : keySet) {
System.out.println(string+"===="+map.get(string));
}
- entrySet()
该方法将 Map 集合的每个元素(每个键值对)为一个整体,作为 Set 的一个集合展现。该方法返回值类型是Set<Map.Entry<K,V>>
,这就说明Entry
是 Map 接口的一个内部接口。
遍历 Map 集合
// entrySet 方法来遍历
Set<Entry<String,Integer>> entrySet = map.entrySet();
Iterator<Entry<String,Integer>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Entry<String,Integer> entry = iterator.next();
System.out.println(entry.getKey()+"----"+entry.getValue());
}
System.out.println("--------------------");
// 增强 for 循环遍历
for (Entry<String, Integer> entry : entrySet) {
System.out.println(entry.getKey()+" == "+entry.getValue());
}
LinkedHashMap 和 TreeMap
- 前者的底层原理和 HashMap 一样,是哈希算法,依靠 hashCode() 和 equals() 方法来给元素排序;而后者则和 TreeSet 的底层原理相同,二叉树算法,依靠比较器给元素排序。
- 前者是可以保证元素怎么存入,就怎么取出来(顺序不变)
HashMap 和 Hashtable
- 共同点
底层都是哈希算法,都是键值对形式存储元素。 - 不同点
- HashMap 是线程不安全的,效率高,JDK1.2版本;
- HashMap 的键和值都可以为 null.
- Hashtable 是线程安全的,效率低,JDK1.0版本;
- Hashtable 的键和值都不能为 null.
总结