05-Java基础-集合框架& List & Map
集合框架、generic泛型、List、Set、Map、Collections类
集合框架
数组和集合的区别:
1.数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值;
集合只能存储引用数据类型(对象)集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象;
2.数组长度是固定的,不能自动增长;
集合的长度的是可变的,可以根据元素的增加而增长;
数组和集合什么时候用?
1.如果元素个数是固定的推荐用数组;
2.如果元素个数不是固定的推荐用集合;
集合框架体系
Set和List的区别
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
- List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。
-
Collection类
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
boolean add(E e) // 添加指定元素
boolean remove(Object o) // 删除指定元素
void clear() //清空集合
boolean contains(Object o) // 是否包含指定元素
boolean isEmpty() // 是否为空
int size() //获取元素个数
toArray() // 把集合转成数组
boolean addAll(Collection c) // c1.addAll(c2) 把c2中元素添加到c1中
boolean removeAll(Collection c) // c1.removeAll(c2) 删除c1中c1、c2的交集
boolean containsAll(Collection c) // c1.containsAll(c2) c1是否包含c2全部元素
boolean retainAll(Collection c) // c1.retainAll(c2) 取交集,并将交集赋值给c1,如果c1改变了,返回true,否则返回false。(如果c1是c2的子集,返回false)
- 集合遍历
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用foreach遍历List
for(String str:list){ //也可以改写for(int i=0;i<list.size();i++)这种形式
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[]strArray = new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) //这里也可以改写为 foreach(String str:strArray)这种形式
{
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator<String> ite = list.iterator();
while(ite.hasNext())//判断下一个元素之后有值
{
System.out.println(ite.next());
}
- ArrayList
ArrayList arrayList = new ArrayList();
for (int i = 0; i < 10; i++) {
//添加元素
arrayList.add(i);
}
// 移除下标为1的元素 并将删除的元素返回
arrayList.remove(1);
// 移除指定元素 并将删除的元素返回
arrayList.remove("A");
// 返回指定元素的最小下标
arrayList.indexOf(3);
// 下标 0 - 3的元素
arrayList.subList(0, 3);
// 数组中元素个数
arrayList.size();
// 替换指定下标元素
arrayList.set(0, "V");
// 并发修改异常产生的原因及解决方案
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("d");
list.add("e");
/*Iterator it = list.iterator();
while (it.hasNext()) {
String str = (String) it.next();
if (str.equals("world")) {
list.add("javaee"); //这里会抛出ConcurrentModificationException并发修改异常
}
}*/
ListIterator lit = list.listIterator(); //如果想在遍历的过程中添加元素,可以用ListIterator中的add方法
while (lit.hasNext()) {
String str = (String) lit.next();
if (str.equals("world")) {
lit.add("javaee");
//list.add("javaee");
}
}
- ListIterator
boolean hasNext() 是否有下一个
boolean hasPrevious() 是否有前一个
Object next() 返回下一个元素
Object previous(); 返回上一个元素
- Vector
// 添加
Vector vector = new Vector();
vector.add("A");
vector.add(2);
vector.add(0, "C");
System.out.println(vector); // [C, A, 2]
Vector vector1 = new Vector(vector);
System.out.println(vector1); // [C, A, 2]
Vector vector2 = new Vector();
vector2.add("R");
vector2.addAll(vector); // 将vector中的所有元素添加到vector2中
vector2.add(vector); // 将vector添加到vector2中
System.out.println(vector2); // [R, C, A, 2, [C, A, 2]]
// 删除
vector2.remove(0); // 删除指定位置的元素
System.out.println(vector2); // [C, A, 2, [C, A, 2]]
vector2.remove("A"); // 删除指定元素的第一个匹配项,如果不包含该元素,则元素保持不变。
System.out.println(vector2); // [C, 2, [C, A, 2]]
vector2.removeAll(vector); // 移除包含在 vector 中的所有元素。
System.out.println(vector2); // [[C, A, 2]]
// 修改
System.out.println(vector); // [C, A, 2]
vector.set(0, "first");
System.out.println(vector); // [first, A, 2]
// 查询
vector.size(); // 返回vector中有几个元素
vector.isEmpty(); // vector是否为空
vector.get(1); // 查找指定索引位置的元素
vector.toArray(); // 将vector对象转换为数组
- LinkedList
实现单向队列和双向队列的接口,自身提高了栈操作的方法,链表操作的方法。LinkedList不擅长查询,尽量不要用index。
LinkedList linkedList = new LinkedList();
for (int i = 0; i < 10; i ++) {
linkedList.add(i);
}
// 链表的第一个元素
linkedList.getFirst();
// 链表最后一个元素
linkedList.getLast();
// 移除
linkedList.remove(0);
linkedList.removeFirst();
linkedList.removeLast();
// 如果不指定索引的话,元素将被添加到链表的最后.
linkedList.add(1, 0);
linkedList.addFirst("A");
linkedList.addLast("B");
// 获取元素下标
linkedList.indexOf(0);
// 替换指定下标元素
linkedList.set(0, "C");
- List的三个子类的特点
- ArrayList:
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。 - Vector:
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。
Vector相对ArrayList查询慢(线程安全的)
Vector相对LinkedList增删慢(数组结构) - LinkedList:
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
查询多用ArrayList
增删多用LinkedList
如果都多ArrayList
generic泛型
泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
- 三种迭代的能否删除
普通for循环,可以删除,但是索引要--
迭代器,可以删除,但是必须使用迭代器自身的remove方法,否则会出现并发修改异常
增强for循环不能删除
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("a");
arrayList.add("b");
arrayList.add("b");
arrayList.add("b");
arrayList.add("c");
// 普通for循环删除元素
for (int i = 0; i < arrayList.size(); i++) {
if ("b".equals(arrayList.get(i))){
arrayList.remove(i);
i --;
}
}
// 迭代器删除
// 方式一
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()){
if ("b".equals(iterator.next())){
iterator.remove();
}
}
System.out.println(arrayList);
// 方式二
for (Iterator<String> iterator1 = arrayList.iterator(); iterator1.hasNext();){
if ("b".equals(iterator1.next())){
iterator1.remove();
}
}
System.out.println(arrayList);
// 增强for循环(for - each)不能删除,只能遍历
- 可变参数
这里的变量其实是一个数组.
如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是最后一个.
格式:
修饰符 返回值类型 方法名(数据类型… 变量名){}
int[] a = {11, 22, 33, 44};
test1(a);
test(a);
test(1,2,3,4);
private static void test1(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
private static void test(int ... arr){
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
- Arrays工具类的asList()
// 数组转换为List,不能增删元素,可以使用集合的其他方法
String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
Integer[] arrs = {1,2,3};
List<Integer> list2 = Arrays.asList(arrs);
// 基本数据类型数组 转 List时,将整个数组作为一个对象存入List
int[] arra = {1,2,3,4};
List<int[]> list1 = Arrays.asList(arra);
- List 转数组
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("a");
arrayList.add("b");
arrayList.add("c");
// 当List转数组时,数组长度如果 <= List.size() ,转换后的数组长度等于List的大小
// 数组长度如果 > List.size() ,分配的数组长度就与指定的长度一样,会多几个null元素
String[] arr = arrayList.toArray(new String[0]);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
Set
- HashSet
一个按着Hash算法来存储集合中的元素,其元素值可以是NULL。无序的,不能重复存储。
HashSet<String> set = new HashSet<String>();
// add(value)方法,可以向set中添加一个元素
set.add("AA");
set.add("BB");
set.add("CC");
set.add(null);
// addAll方法,可以将一个集合整体加入到set中
HashSet<String> sets = new HashSet<String>();
sets.addAll(set);
// remove方法,可以删除指定的一个元素。
set.remove("BB");
// removeAll方法,可以从set中批量删除一部分数据。
sets.removeAll(set);
// clear方法,可以快速清空整个set。
sets.clear();
// size方法,获取set中元素的个数
set.size();
// isEmpty方法,判断set对象是否为空
set.isEmpty();
HashSet存储自定义对象,保证元素唯一性方法:
重写hashCode()和equals()方法:
编辑区 右键 Generate(Command + N),选择equals()and hashCode()
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Animal)) return false;
Animal animal = (Animal) o;
return age == animal.age && Objects.equals(name, animal.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
-
LinkedHashSet
底层是链表实现的,是set集合中唯一一个能保证怎么存就怎么取的集合对象.
因为是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样.
允许null元素,并支持所有可选的Set操作。
像HashSet一样,LinkedHashSet不是线程安全的,所以多个线程的访问必须通过外部机制(如synchronizedSet(Set))进行同步。 -
TreeSet
TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一。
- 当compareTo方法返回0的时候集合中只有一个元素
- 当compareTo方法返回正数的时候集合会怎么存就怎么取
- 当compareTo方法返回负数的时候集合会倒序存储
使用方式
1.自然顺序(Comparable)
TreeSet类的add()方法中会把存入的对象提升为Comparable类型
调用对象的compareTo()方法和集合中的对象比较
根据compareTo()方法返回的结果进行存储
public class Person implements Comparable<Person>{
private String name;
private int age;
@Override
public int compareTo(Person o) {
int length = this.name.length() - o.name.length(); //比较长度为主要条件
int num = length == 0 ? this.name.compareTo(o.name) : length; //比较内容为次要条件
return num == 0 ? this.age - o.age : num;
}
}
2.比较器顺序(Comparator)
创建TreeSet的时候可以制定 一个Comparator
如果传入了Comparator的子类对象, 那么TreeSet就会按照比较器中的顺序排序
add()方法内部会自动调用Comparator接口中compare()方法排序
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
两种方式的区别
TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)
TreeSet如果传入Comparator, 就优先按照Comparator
-
Map
将键映射到值的对象
一个映射不能包含重复的键
每个键最多只能映射到一个值
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1"); // 如果存入的key存在,将被覆盖的值返回
map.put("key2", "value2");
map.put("key3", "value3");
map.put("key4", "value4");
System.out.println(map); // {key1=value1, key2=value2, key3=value3, key4=value4}
// Removes all elements from this Map
// map.clear();
// System.out.println(map); // {}
// Map中是否包含指定的key
map.containsKey("key1")
// Map中是否包含指定的value
map.containsValue("value1");
// Map中所有映射集合
map.entrySet(); // [key1=value1, key2=value2, key3=value3, key4=value4]
// 根据key获取元素
map.get("key2");
// 是否为空
map.isEmpty();
// key的集合
map.keySet();
// values集合
map.values();
// 移除key对应元素 返回值为删除的值
map.remove("key1");
// 集合元素个数
map.size();
- 遍历HashMap
// 方式一
for (String key : map.keySet()) {
System.out.println(map.get(key));
}
// 方式二 比方式一节约资源
for (Map.Entry<String, Integer> en : map.entrySet()) {
String key = en.getKey();
Integer value = en.getValue();
System.out.println("key = " + key + ", value = " + value);
}
Collections类
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("a");
arrayList.add("d");
arrayList.add("c");
arrayList.add("b");
// 排序
Collections.sort(arrayList);
System.out.println(arrayList);
// 二分法查找 返回下标 不存在返回负的插入点-1(负数)
int i = Collections.binarySearch(arrayList, "c");
System.out.println(i);
// 获取最大值
Collections.max(arrayList);
// 获取最小值
Collections.min(arrayList);
// 集合反转
Collections.reverse(arrayList);
// 随机置换顺序
Collections.shuffle(arrayList);
- 泛型固定下边界
?super E - 泛型固定上边界
?extend E