集合
1. 概念
集合和数组一样用于保存多个数据,集合是通过算法实习的一种容器,数组是编程语言内置的容器,数组的元素个数是固定的,集合的长度是随着元素的增长而增长。集合提供了大量的api简化了编程的复杂度。
数据结构: 常见的如: 数组、链表、堆、栈、树、图
这些数据结构和编程语言无关。不同的编程语言可以通过算法逻辑实现这些数据结构。
2. 集合体系
- Collection : 集合跟接口,他抽取了了集合的最基本操作
- List : Collection 的子接口,扩展了有序集合的规范,支持下标访问操作,支持可重复元素
- Set: Collection 的子接口,基本上没有做扩展,它是无序接口的规范,不支持接口的规范,不支持下标,元素不可重复。
3.Collection 接口方法
boolean add(E e)
确保此集合包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合(可选操作)。
void clear()
从此集合中删除所有元素(可选操作)。
boolean contains(Object o)
如果此集合包含指定的元素,则返回 true 。
boolean containsAll(Collection<?> c)
如果此集合包含指定 集合中的所有元素,则返回true。
boolean isEmpty()
如果此集合不包含元素,则返回 true 。
Iterator<E> iterator()
返回此集合中的元素的迭代器。
boolean remove(Object o)
从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
boolean removeAll(Collection<?> c)
删除指定集合中包含的所有此集合的元素(可选操作)。
boolean retainAll(Collection<?> c)
仅保留此集合中包含在指定集合中的元素(可选操作)。
int size()
返回此集合中的元素数。
Object[] toArray()
返回一个包含此集合中所有元素的数组。
4.List接口方法
void add(int index, E element)
将指定的元素插入此列表中的指定位置(可选操作)。
boolean addAll(int index, Collection<? extends E> c)
将指定集合中的所有元素插入到此列表中的指定位置(可选操作)。
E get(int index)
返回此列表中指定位置的元素。
int indexOf(Object o)
返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
int lastIndexOf(Object o)
返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
E remove(int index)
删除该列表中指定位置的元素(可选操作)。
E set(int index, E element)
用指定的元素(可选操作)替换此列表中指定位置的元素。
List<E> subList(int fromIndex, int toIndex)
返回此列表中指定的 fromIndex (含)和 toIndex之间的视图。
5.Set接口
api 几乎和Collection 一致。
6.有序集合实现类
ArrayList :
特点: 查询效率高,增删效率低 ,线程不安全。
原理: 底层数组实现
jdk1.2 新增
Vector :
特点: 查询效率高,增删效率低 ,线程安全。
原理: 底层数组实现
jdk1.0 新增
LinkedList:
特点: 查询效率低,增删效率高 ,线程不安全。
原理: 底层链表实现
jdk1.0 新增
7.泛型
- 类型参数化的一种机制,使用时在指定类型,可以提高代码的复用性、通用性,一般用于封装通用工具。
- java集合中都支持泛型
- 泛型可以定义:泛型类、泛型接口、泛型方法、 泛型参数
示例:
定义泛型类:
public class AAA<T>{
//内部可以使用T 当做数据类型做一切类型可做的事情
}
定义接口:
public interface AAA<T>{
//内部可以使用T 当做数据类型做一切类型可做的事情
}
定义泛型方法
public class AAA{
public <T> T method(T t){
//
}
}
泛型限定:
public void method(List<String> args ){ // 指定类型集合的参数
}
public void method(List<?> args ){ // 任意类型的泛型集合
}
public void method(List<? extends Number> args ){ // 指定 Number 及其子类的泛型集合
}
public void method(List< ? super Integer > args){ // 指定 Number 及其父类的泛型集合
}
基本数据类型不支持泛型,可以使用包装类代替
8.Collection集合工具类
提供了集合操作的一些方法。
public static void main(String[] args) {
List<String> list = new LinkedList();
list.add("ABCD");
list.add("ABCE");
list.add("ABCF");
list.add("ABCG");
//1.反转
Collections.reverse(list);
System.out.println(list);
//2.打乱
Collections.shuffle(list);
System.out.println(list);
//4.排序
List<Integer> nums = new Vector<Integer>();
nums.add(12);
nums.add(1);
nums.add(120);
nums.add(44);
Collections.sort(nums);
System.out.println(nums);
}
9.Set实现类
HashSet:底层使用HashMap,利用Map的key来保存元素
去重原理:添加时先判断hashcode时后一致,不一致直接保存,一致进一步判断equals(可重写,定义自己的相等规则),相等是为重复,放弃存储,若不同则存放
自定义类型去重重写 Hashcode 和 equals
LinkedHashSet:底层LinkedHashSet来实现,可以记录元素插入顺序
TreeSet: 底层使用TreeMap实现,可以对元素排序
- 排序方案: 元素类型 实现Comparetor接口重写CompareTo(T 0)
- 构造器传入 Comparetor 对象 重写 Compare(T o1,T o2);
对于List排序可以使用Collections.sort(List list,Comparator cpr)
10.迭代器
全体集合都可以获得迭代器通过 iterator(): iterator
iterator接口提供了两个方法
- hasNext(): boolean 判断集合中是否还有可获取元素
- next(): 获得当前元素
Iterator<String> it = arr.iterator();
//判断是否还有可获取的元素
while( it.hasNext() ) {
String s= it.next();
System.out.println(s);
}
迭代器原理:
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
调用next方法底层就是操作数组的下标,并返回元素
11.Map实现类
map集合主要是保存一种映射关系,键对值的关系。常见非法:
clear()
从该地图中删除所有的映射(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射,则返回 true 。
boolean containsValue(Object value)
如果此地图将一个或多个键映射到指定的值,则返回 true 。
Set<Map.Entry<K,V>> entrySet()
返回此地图中包含的映射的Set视图。
V get(Object key)
返回到指定键所映射的值,或 null如果此映射包含该键的映射。
boolean isEmpty()
如果此地图不包含键值映射,则返回 true 。
Set<K> keySet()
返回此地图中包含的键的Set视图。
V put(K key, V value)
将指定的值与该映射中的指定键相关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
V remove(Object key)
如果存在(从可选的操作),从该地图中删除一个键的映射。
int size()
返回此地图中键值映射的数量。
Collection<V> values()
返回此地图中包含的值的Collection视图。
12 HashMap
HashMap: 底层通过哈希表(数组加链表)实现,线程不安全,执行下O效率高。
在jdk8中引入红黑树,如果链表长度超过8则转成树,提高查询效率。
存值原理:
添加元素方法:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
计算hash值方法:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
存值流程:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0) // 判断数组是否需要初始化
n = (tab = resize()).length; //resize() 初始化
if ((p = tab[i = (n - 1) & hash]) == null) // 计算下标,判断位置处是否有元素
tab[i] = newNode(hash, key, value, null);//没有元素直接存
else {
Node<K,V> e; K k;
// 如何key相同,把原来NOde记录下
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 如果此位置是一个树结构
else if (p instanceof TreeNode)
//添加到树结构中
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
// 如果此位置是一个链表则添加到链表末尾
else {
for (int binCount = 0; ; ++binCount) {
//查链表,把元素添加到链表末尾
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//如果链表如果超过8 则把链表转成红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// 如果这元素与 链表上的元素相等, 结束遍历
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
//交换地址,继续搜索
p = e;
}
}
// key 已经存在, 覆盖原来的值。
if (e != null) { // existing mapping for key // 保存旧值
V oldValue = e.value;
// 覆盖旧值
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
//返回旧值
return oldValue;
}
}
//操作数 ++
++modCount;
//容量+1 如果操作临界值,则扩容。
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
map便利
public class TestHashMap {
public static void main(String[] args) {
//实例化
HashMap<String,String> map = new HashMap<>();
//添加元素:
map.put("swk", "孙悟空");
map.put("zbj", "猪八戒");
map.put("ts", "唐僧" );
//获得元素个数: 键值对个数
System.out.println( map.size() );
//根据 key 获得 value
String value = map.get("swk");
System.out.println(value);
//移除跟进key
map.remove("zbj");
//获得全部的 key
System.out.println("----------------------");
Set<String> keys = map.keySet();
for(String k : keys ) {
System.out.println(k);
}
//获得全部的 value
System.out.println("-----------------------");
Collection<String> values = map.values();
for(String v : values) {
System.out.println(v);
}
//map遍历方式:获得Entry集合
System.out.println("------------------------");
Set< Entry<String, String> > entrys = map.entrySet();
for( Entry<String, String> entry : entrys ) {
System.out.println( entry.getKey()+":"+entry.getValue() );
}
//map遍历方式:通过keySet集合 配合 get(key)
System.out.println("-------------------------");
for( String key : map.keySet() ) {
System.out.println( key+":"+ map.get(key) );
}
//检测方法,时候为空集合
System.out.println( map.isEmpty() );
System.out.println( map.containsKey("ts") );
System.out.println(map.containsValue("孙悟空"));
}
}
12. Hashtable
早期保存键值对的集合,底层也是使用哈希表算法实现,是Dictionary 字典类的子类,线程安全,执行效率低,不允许使用null键和null值
13. Properties
properties是Hashtable的子类,主要保存配置文件,键和值都是String,可以读取配置文件 ****.properties
public static void main(String[] args) throws FileNotFoundException, IOException {
//实例化集合
Properties pro = new Properties();
//手动添加键值对
pro.put("abc", "hello");
//通过IO流读取配置文件
pro.load( new FileInputStream("src/map/config.properties") );
//遍历
for( Object key : pro.keySet() ) {
System.out.println(key+":"+ pro.get(key) ); ;
}
}
配置文件
ip=192.168.1.90
prot=8080
14.Treemap
可实现,对键进行排序,实际上TreeSet 底层使用的就是TreeMap (了解)。