集合
集合的概述
程序存储数据的方式:
- 变量 : 单一的数据
- 数组:一组具有相同数据类型的数据
有两个问题待解决:
元素的数据类型都是相同的(可以通过对象解决)
数组长度不可变(通过集合解决)
- 对象:一组具有关联性的数据
- 集合:强大的数据储存
集合的体系结构
集合框架是Java官方提供的工具,所以集合框架在 java.util 包里面
接口Collection:储存的是可重复、无序的数据
- 接口List:储存可重复、有序的数据(下标和数据)
实现类
Arraylist(以数组为底层数据结构,查询块,增删慢)
LinkedList(以链表为底层数据结构,查询慢,增删块)
...
- 接口Set:储存的是不可重复、有序的数据(数据,根据哈希值排序)
实现类
HashSet
TreeSet
...
- 接口Map:储存的是键值对类型的数据(Key是唯一的,Value是可重复的。并且Map集合是无序的)
实现类
HashMap
hashTable
Propertie
TreeMap
...
List集合
ArrayList
构造方法:
ArrayList<E>()
常用方法:
- add(E e) : boolean 存储指定类型(E:泛型类型)的元素
- add(int index, E element) : void 在指定下标位置存储指定类型的元素,原有位置的元素会依次后移
- remove(int index) : E 删除指定下标位置的元素,返回被删除的元素
- remove(Object obj) : boolean 删除指定的元素(如果有重复的,只会删除第一个找到的元素)
- set(int index, E element) : E 修改指定下标位置的元素,返回被替换的元素
- get(int index) : E 根据下标查询元素
- size() : int 查询集合中存储的元素数量
- contains(Object o) : boolean 判断集合中是否存在指定的元素
- indexOf(Object o) : int 查询指定元素在集合中的下标,找不到会返回 -1
- lastIndexOf(Object o) : int 从后往前查找指定元素在集合中的下标,找不到会返回-1
- clear() : void 清空集合
- isEmpty() : boolean 判断集合是否是空的(没有一个元素)
LinkedList
构造方法:
LinkedList<E>()
常用方法:
- addFirst(E e) : void 向前追加元素
- addLast(E e) : void 向后追加元素
- removeFirst() : E 删除第一个元素
- removeLast() : E 删除最后一个元素
- getFirst() : E 查询第一个元素
- getLast() : E 查询最后一个元素
注意:
方法名虽然相同,代表的只是方法功能一致,但方法实现不一定一致!
ArrayList和LinkedList的区别(面试题)
相同点:
- 都是 List 接口的实现类,可以存储可重复的,有序的数据
不同点:
- ArrayList 底层数据结构是数组结构,在实现数据的查询时,因为数组结构有下标,所以效率较高。但是,在增加或删除数据时,因为数组结构长度不可变,所以会创建一个新的数组,导致效率较低。
它适合于查询较多,增删较少的情况。
- LinkedList 底层数据结构是链表结构,在增加或删除数据时,尤其是首尾的增删,因为链表结构增加节点或删除节点,只需要修改下一个连接的地址即可,效率较高。但是,在实现数据的查询时,没有下标,只能从首节点依次进行查询,效率较低。
它适合于增删较多,查询较少的情况。
Set集合
HashSet
构造方法:
- HashSet<E>()
常用方法:
- add(E e) : boolean 存储元素(去重数据)
- remove(E e) : E 删除数据
- size() : int 获取集合的元素数量
- contains(Object o) : boolean 判断是否包含指定的元素
注意:
如果用 Set 存储引用数据类型数据,需要重写相应类的 equals 和 hashCode 方法。不然,不认为是重复数据,不会去重。
Map集合
HashMap
构造方法
- HashMap<K,V>()
常用方法
- put(K key, V value) : V 存储/更新(相同键再次调用该方法)键值对数据,返回被替换的值(如果是存储,那么返回的是 null)
- get(Object key) : V 根据键获取值
- size() : int 获取集合中存储的键值对数量
- containsKey(Object key) : boolean 判断集合中是否包含指定的键
- containsValue(Object value) : boolean 判断集合中是否包含指定的值
- remove(Object key) : V 根据键删除,返回的是被删除的值
- keySet() : Set<K> 获取所有键的集合(键是唯一的,所以是Set集合)
- entrySet() : Set<Entry<K,V>> 获取所有键值对的集合
- Entry:
- getKey() : K 获取 Entry 中的键
- getValue() : V 获取 Entry 中的值
- Entry:
- values() : Collection<V> 获取所有值的集合(Map存储,值是无序的,但是可重复,所以是Collection集合)
- clear() : void 清空集合中的键值对
集合的遍历
List集合遍历:
方法1:使用for循环遍历
方法2:使用for-each循环遍历
方法3:使用迭代器遍历
List list = new ArrayList();
// 1.使用for循环遍历
System.out.println("--1.使用for循环遍历--");
for (int i = 0; i < list.size(); i++) {
System.out.printf("读取集合元素(%d): %s \n", i, list.get(i));
}
// 2.使用for-each循环遍历
System.out.println("--2.使用for-each循环遍历--");
for (Object item : list) {
String s = (String) item;
System.out.println("读取集合元素: " + s);
}
// 3.使用迭代器遍历
System.out.println("--3.使用迭代器遍历--");
Iterator it = list.iterator();
while (it.hasNext()) {
Object item = it.next();
String s = (String) item;
System.out.println("读取集合元素: " + s);
}
Set集合遍历
方法1:使用for-each循环遍历
方法2:使用迭代器遍历
Set set = new HashSet();
// 1.使用for-each循环遍历
System.out.println("--1.使用for-each循环遍历--");
for (Object item : set) {
String s = (String) item;
System.out.println("读取集合元素: " + s);
}
// 2.使用迭代器遍历
System.out.println("--2.使用迭代器遍历--");
Iterator it = set.iterator();
while (it.hasNext()) {
Object item = it.next();
String s = (String) item;
System.out.println("读取集合元素: " + s);
}
Map集合的遍历
方法1:通过 getKey() 获取所有的键,通过键获取对应的值
方法2:通过 entrySet() 获取所有的键值对,分别打印键值
方法3:通过1.8的新增的 forEach () 方法,获取所有的键值
Map<K, V> map = new HashMap<>();
// 1:通过 getKey() 获取所有的键,通过键获取对应的值
Set<String> keySet = map.keySet();
for (String key : keySet) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
// 2:通过 entrySet() 获取所有的键值对,分别打印键值
Set<Entry<String,String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
// 3:通过1.8的新增的 forEach () 方法,获取所有的键值
map.forEach((key, value) -> {
System.out.println(key + "=" + value);
});
泛型(JDK1.5出现的特性)
泛型的作用
给集合限制元素的类型,有效的解决类型转换问题
泛型的定义位置
- 类上
// 集合
public class ArrayList<E> {
// 在创建集合对象时,只要指定了 E 的具体类型,那么 add 方法的参数或返回值也就指定了对应的类型
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public E remove(int index) {
}
}
- 方法上
// Arrays类的方法
// 在调用该方法时,传入什么类型数据,返回的就是什么类型的数据
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
"..." 表示为可变参数,可以理解为代表一个可变长度的数组
- 泛型可以限制数据类型必须是某个类的子类
<? extends 类型>
注意:
- 泛型使用任何符号代表都可以。这些符号就是一个某种类型的表示。
- 常见的符号:
- E(Element 元素)、T(Type 类型)、K(Key 键)、V(value 值)
- 常见的符号:
- 泛型在指定具体类型时,必须是引用数据类型
- Java给基本数据类型提供了相应的引用类型/包装类型
- byte -> Byte
- short -> Short
- int -> Integer
- long -> Long
- float -> Float
- double -> Double
- char -> Character
- boolean -> Boolean
- Java给基本数据类型提供了相应的引用类型/包装类型
Collections工具类
常用方法:
- sort(List<T> list) : void 可以实现对集合的升序排序
- min(Collection<?extends T> coll) : T 获取集合中的最小值
- max(Collection<?extends T> coll) : T 获取集合中的最大值
- reverse(List<?> list) : void 反转集合元素
- shuffle(List<?> list) : void 打乱集合元素
Collections和Collection的区别(面试题)
- Collection 是一个集合的顶级接口,用于存储可重复的,无序的数据
- Collections 是一个集合的工具类,里面定义了大量的静态方法,用于更为便捷的操作集合
ArrayLIst的扩容原理(面试题)
- 默认(不传参数)的集合仅仅创建集合对象,没有数据是:内部的数组会初始化为一个静态常量(空数组)
( Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; )
- 默认(不传参数)的集合初始化容量:10(长度为10的数组)
( int DEFAULT_CAPACITY = 10; )
- 进行扩容时,每次扩容:1.5 倍( oldCapacity + (oldCapacity >> 1) )
D10 >> 1 ==> B1010 >> 1 == B101 == D5
注意:
1.集合初始化时, 指定集合初始值大小;
ArrayList<Integer> list = new ArrayList<>(3);
HashMap 使用HashMap(int initialCapacity) 初始化时,无法确定集合的大小,那么指定集合默认值为16
注:initialCapacity = (需要存储的元素个数 / 负载因子) + 1
负载因子默认值为 0.75
HashMap<String, String> map = new HashMap<>(16);