集合和泛型
文章目录
继承图
collection接口
方法
迭代器
iterator()
iterator返回一个Iterator类。拥有的方法如下图。
Collection<String> coll = new ArrayList<>();
coll.add("hello");
coll.add("world");
Iterator<String> itr = coll.iterator();
while(itr.hasNext()){
//注意:最好遍历一次取一次,否则可能发生NoSuchElementException
String next = itr.next();
System.out.println(next);
}
hello
world
迭代器并发修改异常
ConcurrentModificationException。在迭代的同时增删元素。
Collection的迭代器是快速失败的。在next()方法第一句会调用checkForComodification()方法。这个方法比较修改值。如果modCount和expectedModCount的值不一致,程序会抛出了ConcurrentModificationException异常。
Collection<String> coll = new ArrayList<>();
coll.add("hello");
coll.add("world");
Iterator<String> itr = coll.iterator();
while(itr.hasNext()){
String next = itr.next();
list.remove("hello"); //删除元素
list.add("exception"); //增加元素
}
解决方法:调用iterator的remove()方法。这个方法会对expectedModCount进行重新赋值。
1 public void remove() {
2 if (lastRet == -1)
3 throw new IllegalStateException();
4 checkForComodification();
5
6 try {
7 AbstractList.this.remove(lastRet);
8 if (lastRet < cursor)
9 cursor--;
10 lastRet = -1;
11 expectedModCount = modCount; //重新赋值
12 } catch (IndexOutOfBoundsException e) {
13 throw new ConcurrentModificationException();
14 }
15 }
foreach循环遍历
可以实现对数组或者集合进行快速遍历。缺点是不能获取索引。数组遍历和集合遍历的原理有所不同。数组遍历采用fori循环。集合遍历则使用迭代器。
- 数组遍历
type[] arr;
for (type var : arr) {
body-of-loop
}
//---------------反编译如下------------------------
for (int i = 0; i < arr.length; i++) {
type var = arr[i];
body-of-loop
}
- 集合遍历
Collection<type> coll;
for (type var : coll) {
body-of-loop
}
//---------------反编译如下------------------------
for (Iterator<type> iter = coll.iterator(); iter.hasNext(); ) {
type var = iter.next();
body-of-loop
}
泛型
- 泛型类
public class Test<E>{
public E content;
public void show(E msg){
System.out.println(msg);
}
}
- 泛型方法
//泛型方法在方法名称前面有一个<T>声明,它的作用是告诉编译器编译的时候就识别它的类型,如果传入的T是A类型,那么你就不可以将B类型传入方法中去;
public static <T> T test(T param){
return param;
}
- 泛型接口
继承泛型接口有两种方式,第一个是指定接口的类型,定义类是普通类。第二个不指定接口的类型,定义类也是泛型类。
public interface Dao<T>{
public void save (T t);
public void update (T t);
}
- 泛型上限
//继承Number都可以使用
ArrayList<? extends Number> list;
- 泛型下限
//Integer的超类都可以使用
ArrayList<? super Integer> list;
List
java.util.list
接口集成自Collection
接口,是单列集合的重要分支。其特点如下:
- list集合占据一段连续的地址空间,以线性方式存储。
- list集合的存放和取出是有序的。
- list集合带有索引,可以根据索引精确地查找元素。
ArrayList
java.util.ArrayList
,内部有一个数组,添加元素时如果数组大小为零会扩容为10,当size要超过数组大小时会扩容1.5倍。
因为内部是数组,所以查改快,增删时要进行数组的整体复制,造成一定的时间开销。因为其特点所以很适合查询多,增删少的场景
LinkedList
java.util.linkedList
内部是一个双向链表。元素增删快,查找慢。
内部维护着首尾两个指针节点。拥有大量关于首尾操作的方法。
public void addFirst(E e) :将指定元素插⼊入此列列表的开头。
public void addLast(E e) :将指定元素添加到此列列表的结尾。
public E getFirst() :返回此列列表的第⼀一个元素。
public E getLast() :返回此列列表的最后⼀一个元素。
public E removeFirst() :移除并返回此列列表的第⼀一个元素。
public E removeLast() :移除并返回此列列表的最后⼀一个元素
Set
java.util.Set
接口也继承Collection
接口,基本上只重写了Collection
中的方法。特点如下
- 元素无序,没有索引,并且不可重复。
HashSet
java.util.HashSet
是一个存储无重复数据的集合,Set接口的实现类。内部是一个数组加链表形成的哈希表。元素存取的顺序是不一致的,通常由元素的hashcode和数组的大小决定其顺序。
java.util.HashSet
内部有一个HashMap
,对元素的增删改查通过调用HashMap方法来完成。
HashSet的存取逻辑如下:
1.通过元素的hashCode方法计算数组索引。
2.如果索引位置没有元素,则直接放置。否则调用元素的equals方法和遍历链表进行内容比较。
3.如果内容相同,则返回旧值;否则,将元素插在链表的尾部。
4.如果链表大小超过8, 则将链表转化成为红黑树(JDK1.8)
5.计算元素的数量是否超过阀值,如果超过则扩容两倍。
LinkedHashSet
java.util.LinkedHashSet
继承HashSet
。内部是由一个哈希表和一个双向链表组成,能够实现存取顺序的一致性。
Collections工具类
常用方法
java.utils.Collections
是集合工具类,用来对集合进行操作。常用方法如下:
public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。
public static void shuffle(List<?> list) 打乱顺序 :打乱集合顺序。
public static <T> void sort(List<T> list) :将集合中元素按照默认规则排序。
public static <T> void sort(List<T> list,Comparator<? super T> c) :使⽤自定义比较器对集合中元素进⾏行行排序。
代码演示
public class CollectionsDemo {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
//原来写法
//list.add(12);
//list.add(14);
//list.add(15);
//list.add(1000);
//采⽤用⼯工具类 完成 往集合中添加元素
Collections.addAll(list, 5, 222, 1,2);
System.out.println(list);
// 对集合元素乱序
Collections.shuffle(list);
System.out.println(list);
//排序⽅方法:升序
Collections.sort(list);
System.out.println(list);
// 使⽤用自定义比较器排序:降序
Collections.sort(list,new MyComparator());
System.out.println(list);
}
}
/*
* 自定义比较器
*/
class MyComparator implements Comparator<Integer>{
public int compare(Integer o1,Integer o2) {
return o2 - o1;
}
}
结果:
[5, 222, 1, 2]
[1, 2, 5, 222]
Map接口
常用方法
public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。
public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
public Set<K> keySet() : 获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
Map的遍历
键找值方式
Map<String,String> map = new HashMap<>();
map.put("邓超", "孙俪");
map.put("李晨", "范冰冰");
map.put("刘德华", "柳岩");
map.put("黄晓明", "Baby");
map.put("谢霆锋", "张柏芝");
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key);
String value = map.get(key);
System.out.println(value);
}
键值对方式(获取Entry对象): 直接获取键值对对象,不用每次都遍历,效率较高。
Map<String,String> map = new HashMap<>();
map.put("邓超", "孙俪");
map.put("李晨", "范冰冰");
map.put("刘德华", "柳岩");
map.put("黄晓明", "Baby");
map.put("谢霆锋", "张柏芝");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println("key: " + entry.getKey());
System.out.println("value: " + entry.getValue());
}