集合
- 集合中不能存储基本数据类型,也不能直接存储java对象,集合中存储的是引用,存的是对象的内存地址。
- 不同的集合底层对应不同的数据结构:数组、二叉树、链表、哈希表等。
- 所有的集合类和集合接口都在java.util包下。
- 集合继承结构图
一类是单个方式存储元素,这一类集合中的超级父接口是:java.util.Collection;
另一类是键值对方式存储元素,这一类集合中的超级父接口是:java.util.Map;
- Collection接口继承Iterable接口,表示所有集合都是可迭代的/可遍历的。Iterable接口中有个iterator()方法,Collection接口继承了iterator()方法,子接口调用这个方法返回一个Iterator类型的对象:
Iterator it = Collection对象.iterator();
,it是迭代器对象,通过这个迭代器对象,可以遍历集合中的元素。 - List
一、ArrayList集合底层采用数组,非线程安全。
二、LinkedList集合底层采用双向链表数据结构。
三、Vector集合底层采用数组,是线程安全的。(几乎不用了) - Set
一、HashSet集合在new的时候实际上new了一个HashMap集合,向HashSet中存储元素实际上是存储到HashMap集合的key部分了。HashMap集合是一个哈希表数据结构。
二、TreeSet集合底层是一个TreeMap,在new的时候实际上new了一个TreeMap集合,向TreeSet中存储元素实际上是存储到TreeMap集合的key部分了。TreeMap底层采用二叉树数据结构。 - SortedSet无序且不可重复,元素自动按照大小排序。
- Map
一、HashMap集合底层是哈希表数据结构。
二、HashTable集合底层是哈希表数据结构,线程安全的,效率较低。(较少使用)
三、TreeMap集合底层是二叉树,key自动按照大小排序。
注:Map中的key实际上就是一个Set,往Set集合中放数据,数据放到Map集合的key部分。
Collection接口
Collection接口常用方法
- Collection中能存放什么元素?
没使用范型时,可以存储Object的所有子类型;使用范型后,只能存储某个具体类型。【集合中不能存储基本数据类型,也不能直接存储java对象,集合中存的是对象的内存地址。】 - .add(元素)、.size()、.clear()、.contains(元素)、.remove(元素)、isEmpty()、toArray()、.iterator()
集合遍历/迭代
- Collection接口继承Iterable接口,表示所有集合都是可迭代的/可遍历的。Iterable接口中有个iterator()方法,Collection接口继承了iterator()方法,子接口调用这个方法返回一个Iterator类型的对象:
Iterator it = Collection对象.iterator();
,it是迭代器对象,通过这个迭代器对象,可以遍历集合中的元素。
注意:当集合结构发生改变,迭代器需要重新获取,否则会报java.util.ConcurrentModification异常。迭代器对应的是当前状态下的集合(想像成集合某个时间点时的快照),当集合中元素删除了,但没有更新迭代器就会报异常,所以应当通过迭代器调用remove()方法。 - Iterator接口中有三个方法:
.hasNext():返回true表示还有元素可以迭代;False表示没有元素可以迭代了。
.next():将迭代器往下走一位并返回指向的元素。【规定:该方法返回值类型是Object】
.remove():删除迭代器指向的当前元素。
List接口
List接口特有的常用方法:add(索引,元素)、.get(索引)、.indexOf(元素)、.lastIndexOf(元素)、.set(索引,元素)
ArrayList实现类
- ArrayList集合初始化容量是10(底层先创建一个长度为0的数组,当添加了第一个元素后,初始化容量为10),底层是Object类型的数组。
- ArrayList集合的扩容,增长为原容量的1.5倍。尽量一开始指定初始化容量:
List mylist = new ArrayList(100);
,扩容过程效率较低。 - 非线程安全的集合
- 怎么把ArrayList集合转为线程安全的呢?
List mylist = new ArrayList();
Collections.synchronizedlist(mylist);
// java.util.Collection 是接口
// java.util.Collections 是工具类
LinkedList实现类
- 没有初始容量,底层是双向链表。
- 我们要学会面向接口编程,调用接口中的方法
List myList = new LinkedList();
。
Vector实现类
- 底层是数组,初始化容量是10,扩容为原来的2倍。
- Vector所有方法都有Synchronized修饰,线程安全。
泛型
List<E> myList = new ArrayList<E>();
表示集合中只能存放E类型的数据。- 自定义泛型
HashSet实现类
- 无序,无下标,不可重复
- HashSet集合在new的时候实际上new了一个HashMap集合,向HashSet中存储元素实际上是存储到HashMap集合的key部分了。HashMap集合是一个哈希表数据结构。
TreeSet实现类
- 无序,无下标,不可重复,自动排序
- TreeSet集合底层是一个TreeMap,在new的时候实际上new了一个TreeMap集合,向TreeSet中存储元素实际上是存储到TreeMap集合的key部分了。TreeMap底层采用二叉树数据结构。
Map接口
Map接口常用方法
- Map存储键值对,key和value都是引用数据类型,都是存储的对象的内存地址。
- .clear()、.containsKey()、.containsValue()、.put(key,value)、.get(key)、.isEmpty()、.size()
.keySet()获取Map集合中的所有key,返回一个Set集合、
.remove(key)通过key删除键值对、
.values()获取所有的values,返回一个Collection集合、
.entrySet()将Map集合转为Set集合,返回一个set集合【Map遍历是重点】
Map<Integer,String> myMap = new HashMap<>();
myMap.put(1,"zhangsan");
myMap.put(2,"lisi");
myMap.put(3,"wangwu");
// 遍历
Set<Map.Entry<Integer,String>> set = myMap.entrySet();
// 第一种遍历方式:迭代器
Iterator<Map.Entry<Integer,String>> it = set.iterator();
while(it.hasNext()){
Map.Entry<Integer,String> node = it.next();
Integer key = node.getKey();
String value = node.getValue();
System.out.println(key+"="+value);
// 第二种遍历方式(效率更高):foreach
for(Map.Entry<Integer,String> node:set){
Integer key = node.getKey();
String value = node.getValue();
System.out.println(key+"="+value);
}
// 或者先获取key
Set<Integer> keys = myMap.keySet();
// 再去遍历keys,通过每个key去取value
// 可以迭代器遍历或者foreach遍历
for(Integer key:keys){
System.out.println(key+"="+myMap.get(key));
}
- 掌握map.put(key,value)的原理:
第一步:将key,value封装到Node对象中;
第二步:底层调用key的hashCode()方法得出hash值,然后通过hash函数将hash值转换为数组下标,下标位置上如果没有元素就把这个Node添加到这个位置,但如果下标对应位置上有链表,就拿着key和链表上每一个节点的key进行equals,如果所有equals方法返回都是False,则把该节点添加到链表末尾,若其中一个equals方法返回True,则将该节点对应的value覆盖。
HashMap实现类
哈希表/散列表:一维数组,数组中每个元素是一个单向链表。
- HashMap默认初始容量是16,默认加载因子是0.75,也就是当HashMap底层数组容量达到75%时,数组就开始扩容。HashMap初始容量必须是2的倍数,可以达到散列均匀,提高HashMap存取效率。
- 如果一个类的equals方法重写了,那么hashCode方法也必须重写。如果equals返回True,表示两个对象在哈希表的相同数组下标对应的同一个单向链表中,也就说明这两个对象的hashCode值也相等。
- JDK8之后,当哈希表的单向链表中元素个数超过8时,单向链表就会变成红黑树(首先会检查数组长度,如果数组长度没超过64,先进行数组扩容。),当红黑树中节点个数小于6时,再转为单链表。
- HashMap集合key和value都可以为null,但只能有1个null的key。
- 非线程安全
HashTable实现类
- HashTable的key和value都不能为null,报空指针异常。
- 方法都被synchronized修饰,线程安全,效率低,较少使用。
- 初始化容量11,默认加载因子0.75,HashTable的扩容是原容量的2倍+1。
Properties属性类
- 继承HashTable
- 线程安全的
- key和value只支持String类型。
Properties pro = new Properties();
pro.setProperty("url","www.cctv.com"); //setProperty方法 调用了HashTable的put()方法
String url = pro.getProperty("url"); //根据key取值
TreeSet
- TreeSet底层实际上是一个TreeMap
- TreeMap底层是二叉树,遵循中序遍历原则。
- 放到TreeSet集合中的元素实际上放到TreeMap集合的key部分了
- TreeSet集合中元素无序不可重复,但是可以按照元素大小自动排序
- 想实现自定义排序:
必须先实现comparable<E>
接口,并且重写compareTo方法,并且在方法中重写规则;
或者在构造treeSet和treeMap时候传一个比较器对象,重写compare方法。TreeSet ts = new TreeSet(new Comparator<E>(){ public int compare(){比较规则} });
compareTo方法的返回值很重要,返回0表示相同,value会被覆盖;返回大于0,会继续在右子树上找;返回小于0,会继续在左子树上找。
import java.util.Comparator;
import java.util.TreeSet;
// 使用TreeSet集合实现字符串按长度进行排序
// Comparator实现定制比较
public class MyTreeSetExample {
public static void main(String[] args) {
TreeSet<String> treeset = new TreeSet<String>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
int n1 = o1.length()-o2.length();
int n2 = o1.compareTo(o2);
return n1==0?n2:n1;
}
});
treeset.add("aaa");
treeset.add("bbb");
treeset.add("cc");
treeset.add("dddd");
treeset.add("aa");
treeset.add("w");
treeset.add("uuuuuu");
System.out.println(treeset.toString());
}
}
java.util.Collections集合工具类
java.util.Collection 是接口
java.util.Collections 是工具类
- 怎么把ArrayList集合转为线程安全的
List mylist = new ArrayList();
Collections.synchronizedlist(mylist);
// java.util.Collection 是接口
// java.util.Collections 是工具类
Collections.sort(list)