1、集合框架概述
java中为了操作多个对象,需要对多个对象进行存储和管理,针对Array的局限性,java集合就像一个容器可以把多个对象的引用放入容器中。
java集合类就是对多个数据进行存储和操作的类,存储的类型可以是单个对象,也可以是关联关系的对象。主要此时的存储是在内存中,临时存储。
2、分类
java集合分为两种不同的存储体系:
-
Collection(接口):单值操作接口
- List(接口):元素有序,可以重复
- ArraysList、LinkList、Vector
- Set(接口): 元素无序,不可以重复
- HashSet、LinkedHashSet、TreeSet
- List(接口):元素有序,可以重复
-
Map:对值操作接口,由key-value组成,key不能重复,value可以重复,元素(key-value)无序
- hashMap、LinkedHashMap、Hashtable TreeMap Properties
2.1 Collection
2.1.1 体系
JDK不提供次接口的具体实现,而是提供更好的具体的子接口;
JDK5之前,Java集合会丢失容器中所有对象的数据类型,把所有的对象都当成Object类型处理,从JDK1.5之后增加了泛型,集合就可以记住容器中对象的数据类型。
2.1.2 常用方法
- add():添加元素
- size():元素个数
- addAll(Collection c):将c中元素放入本集合
- clear():清空集合所有元素
- isEmpty():集合元素是否为空
- contains(Object o):判断集合中是否包含指定元素,实际调用equals判断是否存在。
- containsAll(Collection<?> c):判断集合c中的元素是否在指定的集合中。
- remove(Object o):移除元素,位置前移,根据equals方法判断
- removeAll(Collection <?>c):从当前集合中删除c中所有的元素。
- retainAll(Collection<?> c):交集,当前集合变化,返回true、false。
- equals(Object o):与集合比较。
- hashCode():返回hashCode值
- toArrays():返回数组
- toArray(T[] ):返回指定类型数组
- asList():返回的集合是Arrays中的内部类定义的ArrayList
向Collection接口的实现类中添加对象时,要求所在的类需要重写equals方法。
collection集合判断元素是否存在会调用对象所在的equals方法。
2.1.3 迭代器接口Iterator
Iterator对象迭代器(设计模式),主要用于遍历集合中的元素。
迭代器是为容器而生的。迭代器创建的前提必须是集合要存在,通过集合创建迭代器。
Collection接口继承了java.lang.Iterable接口,iterator()方法仅仅用于遍历集合,Iterator本身并不提供承装对象的能力。
集合对象每次调用iterator()方法都会得到一个新的迭代器对象,默认游标都在集合的第一个元素之前。
迭代器执行原理:
Collection col = new ArrayList();
col.add(123);
col.add(456);
col.add(new String("Tom"));
col.add(false);
//col.add(new String("Tom"));
Person p = new Person(20,"QQ");
col.add(p);
//1、创建迭代器
Iterator iter = col.iterator();
//方式一:直接获取元素
System.out.println(iter.next());//123
System.out.println(iter.next());//456
System.out.println(iter.next());//tom
System.out.println(iter.next());//fase
System.out.println(iter.next());//Person [name=QQ, age=20]
System.out.println(iter.next());//报错,NoSuchElementException
//方式二:
while(iter.hasNext()) {
System.out.println(iter.next());
}
//迭代器理解: 每次next,游标下移一次,会跳过元素 (报错)
while(iter.next() != null) {
System.out.println(iter.next());
}
//错误,死循环
while(iter.hasNext()) { //游标没有移动
System.out.println(col.iterator().next()); //每次调用iterator()产生新的迭代器
}
//删除remove
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
//iterator.remove(); //报错:IllegalStateException
Object object = iterator.next();
if("Tom".equals(object)){
iterator.remove();
//iterator.remove();报错
}
}
System.out.println(coll);
Iterator可以删除集合中的元素,但是在遍历过程中通过迭代器对象的remove()方法不是集合中的remove方法。
如果还未调用next()方法或多次调用remove方法都会报错。
2.1.4 foreach循环
jdk1.5之后提供了foreach循环迭代访问Collection和数组。
foreach底层使用的迭代器方式。
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(new String("Jack"));
coll.add(false);
//for(数据类型 变量:数组/集合)
for(Object obj : coll){
System.out.println(obj);
}
int[] arr = new int[]{1,2,3,4,5};
//方式一:
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//方式二:增强for循环
for(int i : arr){
System.out.println(i);
}
2.1.5 List
(1)概述
有序、可重复、动态数组,某些场合下代替原有的数组。
核心实现类:ArrayList、LinkedList、Vertor
(2) ArrayList
作为List接口的主要实现类,线程不安全、效率高,底层使用Object[] elementDate数组存储数据。
源码:
/**
* Constructs an empty list with an initial capacity of ten.
*/
//创建一个空数组,保存数据
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//保存数据的数组
transient Object[] elementData;
//添加的方法
public boolean add(E e) {
//数组扩容的方法
ensureCapacityInternal(size + 1);
// Increments modCount!!
//将元素放入数组中
elementData[size++] = e;
return true;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//改变数组大小(容量),扩容方法
grow(minCapacity);
}
ArrayList底层使用Object[] elementData初始化为{},并没有创建长度为10的数组(Jdk1.8)。
第一次调用add()方法后,底层才创建了长度为10的数组。
Arrays.asList(…)方法返回的List集合,既不是ArrayList的实例,也不是Vector的实例,返回的是一个固定长度的List集合。
常用方法:
add(int index, E element) : 指定位置添加元素(index之后元素后移)
get(int index)
indexOf(Object o):根据指定对象寻找下标,判断标准equals
lastIndexOf(Object o) :获取最后一次元素出现的下标
remove(int index) :根据下标删除元素,返回删除的元素
set(int index, E element) : 修改
subList(int fromIndex, int toIndex):获取指定区间的子集左闭区间[ )右开区间
(3) LinkedList
底层使用双向表存储数据,如果频繁添加,删除元素效率比ArrayList高。链表如图:
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first; //第一个节点
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||(last.next == null && last.item != null)
*/
transient Node<E> last; //最后一个节点
//node的内部结构
private static class Node<E> {
E item;
Node<E> next; //下一个node
Node<E> prev; //前一个node
Node(Node<E> prev, E element,Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
//添加的逻辑
public boolean add(E e) {
linkLast(e);
return true;
}
LinkedList内部声明了Node类型的first和last属性,默认值null;
add()方法,将参数封装到Node中,创建了Node对象;
Node的创建,体现了LinkedList双向列表的结构。
独有方法:
LinkedList linkedList = new
LinkedList();
linkedList.add(123);
linkedList.add(456);
linkedList.add("tom");
//addFirst(E e)
//linkedList.addFirst("AA");
// addLast(E e)
// linkedList.addLast("ZZ");
// System.out.println(linkedList);
//peek()
//System.out.println(linkedList.peek());
//peekFirst()
//System.out.println(linkedList.peekFirst());
//poll()
System.out.println(linkedList.poll());
System.out.println(linkedList);
(4) Vertor
Vertor是一个古老的实现类,甚至出现在List接口之前,线程安全(synchronized),效率低,底层使用Object[] elementDate数组实现存储。
protected Object[] elementData; //使用数组保存数据
//初始大小10
public Vector() {
this(10);
}
//扩容方法
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
默认扩容是原来的2倍
(5) 总结
常用方法:
增:add(Object obj)
删:remove(int index) 、remove(Object obj)
改:set(int index, Object obj)
查:get(int index)
插入:add(int index, Object obj)
长度:size()
遍历:Iterator方式、增强for循环、普通for循环
遍历集合:
ArrayList list = new ArrayList(); //可以
指定初始容量
list.add(123);
list.add(456);
list.add(new String("AA"));
list.add(new String("BB"));
list.add(new String("CC"));
//方式一:迭代器
Iterator iter = list.iterator();
while(iter.hasNext()) {
Object obj = iter.next();
System.out.println(obj);
}
//方式二:迭代器+for
for (Iterator iter2 = list.iterator();iter2.hasNext();) {
Object obj = iter2.next();
System.out.println(obj);
}
//方式三:for+get()方法
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.println(obj);
}
//方式四:foreach
for(Object obj : list) {
System.out.println(obj);
}
2.1.6 Set
存储无序的,不可重复的数据。
核心实现类:
- HashSet:
- LinkedHashSet
- TreeSet
(1) HashSet
作为Set的主要实现类,线程不安全,可以存储null值。
存储原理:
-
当向 HashSet 集合中存入一个元素时,HashSet会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)。
-
如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果 为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了, 那么会通过链表的方式继续链接。
-
如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功(违背了set集合不能放重复元素的规则)。
//Set判断元素相同
Set set = new HashSet();
set.add(123);
set.add(456);
set.add("Tom");
set.add(123); //相同元素不能添加
//new Object().hashCode();
//判断两个元素是否相同,先判断hashcode值,再调用equals方法
set.add(new Person("Tom",20));
set.add(new Person("Tom",20));
System.out.println(set);
结论:
- Set接口是Collection的子接口,set接口没有提供额外的方法
- HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能
- HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对
象的 equals() 方法返回值也相等。 - 对于存放在Set容器中的对象,对应的类一定要重写equals() 和hashCode(Objectobj) 方法,以
实现对象相等规则 。即: “相等的对象必须具有相等的散列码“。 - 无序性:表示存储的数据在底层的数组中并非按照数组的索引顺序添加,而是根据数据的哈希值决定。
- 不可重复:为了保证元素的不可重写,我们需要重写hashcode和equals方法。
(3)LinkedHashSet
LinkedHashSet是HashSet的子类。
LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但同时使用双向链表来维护元
素的次序,这使得元素看起来是以添加的顺序保存的。
LinkedHashSet插入的性能略低于HashSet,但迭代访问set的性能较好。
LinkedHashSet不能放入重复元素。
(3)TreeSet
-
TreeSet是SortedSet接口的实现类,TreeSet可以确保元素的集合处于有序状态
-
TreeSet底层使用红黑树结构存储
-
判断两个元素是否重复的标准:
- 自然排序使用: compareTo(Object obj)==0判断相等,不再使用equals()方法。
- 定制排序使用:compare(Object o1,Object o2)判断相等, 不再使用equals()方法。
- 当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与compareTo(Object obj) 方法有一致的结果:如果两个对象通过equals()方法比较返回 true,则通过compareTo(Object obj) 方法比较应返回0。
//自然排序
class Person implements
Comparable<Person> {
private String name;
private int age;
...
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name
== null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() !=
obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if
(!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name +
", age=" + age + "]";
}
@Override
public int compareTo(Person o) {
if(this.age > o.age) {
return 1;
}else if(this.age < o.age) {
return -1;
}else {
//return 0;
return
this.name.compareTo(o.name);
}
}
}
TreeSet<Person> set = new
TreeSet<Person>();
set.add(new Person("Tom", 12));
set.add(new Person("Jim", 30));
//相同的判断标准,comparable返回的值
set.add(new Person("Jack", 30));
set.add(new Person("Jack", 30)); //相同不能添加
set.add(new Person("Jerry", 20));
System.out.println(set);
定制排序:
Comparator<Person> com = new
Comparator<Person>() {
@Override
public int compare(Person o1, Person
o2) {
if(o1.getAge() > o2.getAge()) {
return 1;
}else if(o1.getAge() <
o2.getAge()) {
return -1;
}else {
return
o1.getName().compareTo(o2.getName());
}
}
};
TreeSet<Person> set = new
TreeSet<Person>(com);
set.add(new Person("Tom", 12));
set.add(new Person("Jim", 30));
//相同的判断标准,comparable返回的值
set.add(new Person("Jack", 30));
set.add(new Person("Jack", 30)); //相同不能添加
set.add(new Person("Jerry", 20));
System.out.println(set);
2.2 Map接口
双列数据。存储key-value的对数据
2.2.1 特点
key无序、不可重复,可以使用Set存储所有的key,如果是引用类型需要重写hashCode和equals方法,保证不可重复;
value无序,可重复,可以使用Collection存储所有的value —>如果是引用类型需要重写equals方法。
一个键值对:key-value就构成了一个Map.Entry对象Map中的Entry无序,不可重复,使用Set可以存储Entry。
2.2.2 核心实现类
- HashMap
- LinkedHashMap
- treeMap
- Hashtable
- properties
(1) HashMap
作为Map的主要实现类,线程不安全,效率高,存储null的key-value,底层使用数组+链表(jdk7)、数组+链表+红黑树(jdk1.8)。
(2)LinkedHashMap
保证遍历Map元素时,可以按照添加的顺序实现遍历,因为在原有的HashMap底层结构基础上添加了一对指针,指向前一个和后一个元素,对于频繁的操作,执行效率高于hashMap。
在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序。与LinkedHashSet类似,LinkedHashMap可以维护Map的迭代顺序,迭代顺序与key-value对的插入顺序一致。
//HashMap内部的Node节点
static class Node<K,V> implements
Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
/**
* HashMap.Node subclass for normal
LinkedHashMap entries.
*/
static class Entry<K,V> extends
HashMap.Node<K,V> {
Entry<K,V> before, after; //使用双向链表
Entry(int hash, K key, V value,Node<K,V> next) {
super(hash, key, value,next);
}
(3) Hashtable
作为一个古老的实现类,线程安全,效率低,不能存储null的key和value。
(4) Properties
是Hashtable的子类,主要用于属性文件的配置。
由于属性文件里的key、value都是String类型,所以Properties对象的key和value也是String类型。
存取数据时,建议使用setProperties(String key,String value)保存数据,getProperties(String key)取数据。
// 1、创建流
FileInputStream fis = null;
try {
fis = new FileInputStream("db.properties");
// 2、创建Properties对象
Properties p = new Properties();
// 3、加载流对应的信息
p.load(fis);
//4、根据key获取value
String username = p.getProperty("username");
String password = p.getProperty("password");
String url = p.getProperty("url");
System.out.println("username = " + username);
System.out.println("password = " + password);
System.out.println("url = " + url);
} finally {
if(fis != null) {
fis.close();
}
}
(5) TreeMap
-
TreeMap可以保证所有的key-value处于有序状态
-
TreeMap底层使用红黑树结构保存数据
-
TreeMap的key排序
- 自然排序:TreeMap中所有的key必须实现Comparable接口,而且所有的key为
同一类型对象。 - 定制排序:创建TreeMap对象时,传递一个Comparator对象。
- 自然排序:TreeMap中所有的key必须实现Comparable接口,而且所有的key为
//
class Person implements Comparable<Person> {
private String name;
private int age;
...
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name== null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if(!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name +", age=" + age + "]";
}
@Override
public int compareTo(Person o) {
if(this.age > o.age) {
return 1;
}else if(this.age < o.age) {
return -1;
}else {
//return 0;
return this.name.compareTo(o.name);
}
}
}
TreeMap map = new TreeMap();
map.put(new Person("Tom",20), 20);
map.put(new Person("Jack",30), 20);
map.put(new Person("Carry",40), 20);
map.put(new Person("Tony",10), 20);
定制排序:
Comparator<Person> com = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if(o1.getAge() > o2.getAge()) {
return -1;
}else if(o1.getAge() < o2.getAge()) {
return 1;
}else {
return o1.getName().compareTo(o2.getName());
}
}
}
TreeMap map = new TreeMap(com);
map.put(new Person("Tom",20), 20);
map.put(new Person("Jack",30), 20);
map.put(new Person("Carry",40), 20);
map.put(new Person("Tony",10), 20);
2.2.3 常用方法
Map map = new HashMap();
//添加put
map.put("AA",123);
//修改put
map.put("AA",456);
//添加putAll
Map map2 = new Map().put("BB",123);
map.putAll(map2);
//删除remove(Key k),根据key删除元素,返回值为value
map.remove("AA");
//remove(Key k,Value v)
//clear()
//get(Key k),返回值value
//containsKey(Object key): 判断是否存在key
//containsValue(Object value):判断是否存在value
//遍历
//1、遍历所有的key ---- keySet()获取所有的key
Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
System.out.println("------------");
//2、遍历所有的value ----
values():获取所有的value
Collection collection =map.values();
for(Object obj : collection) {
System.out.println(obj);
}
System.out.println("------------");
//3、遍历所有的key---value entrySet()获取所有的Entry
Set set2 = map.entrySet();
//set集合中装的是Entry --- key:value
Iterator iter2 =set2.iterator();
while(iter2.hasNext()) {
Object obj = iter2.next();
//此时object = Entry
Entry entry = (Entry)obj;
//System.out.println(obj);
System.out.println(entry.getKey() + "---" + entry.getValue());
}
System.out.println("-------------");
//方式二
Set keySet = map.keySet();
for (Iterator iter3 = keySet.iterator(); iter3.hasNext();) {
Object key = iter3.next();//key
Object value =map.get(key); //
System.out.println(key + "---" + value);
}
}
总结:
增: put(Object key, Object value)
删:remove(Object key)
改:put(Object key, Object value)
查:get(Object key)
长度:size()
遍历:keySet()、values()、entrySet()
3、Collections工具类
Collections是一个操作List、set和Map等集合的工具类
提供的许多的静态方法,对集合进行排序,查询,修改等操作,可以将集合对象设置为不可
变,实现同步方法等操作。
常用方法:
public static void sort(List list):排序 默认情况下是自然顺序。
public static int binarySearch(List<?> list,T key):二分查找
public static T max(Collection<?> coll):最大值
public static void reverse(List<?> list):反转
public static void shuffle(List<?> list):随机置换
addAll(Collection<? super T> c, T… elements):添加元素
copy(List<? super T> dest, List<? extends T> src):将所有元素从一个列表复制到另一个列表中。
disjoint(Collection<?> c1, Collection<?> c2):如果两个指定的集合没有共同的元素,则返回 true 。
fill(List<? super T> list, T obj):用指定的元素代替指定列表的所有元素。
frequency(Collection<?> c, Object o):返回指定集合中与指定对象相等的元素数。
indexOfSubList(List<?> source, List<?> target):返回指定源列表中指定目标列表的第一次出现的起始位置,如果没有,则返回-1。
lastIndexOfSubList(List<?> source, List<?> target):返回指定源列表中指定目标列表的最后一次出现的起始位置,如果没有则返回-1。
replaceAll(List list, T oldVal, T newVal):将列表中一个指定值的所有出现替换为另一个。
List<Integer> list = new ArrayList<Integer>();
list.add(20);
list.add(50);
list.add(10);
list.add(60);
list.add(50);
List<Integer> list2 = new ArrayList<Integer>();
list2.add(100);
list2.add(50);
list2.add(30);
Collections.sort(list);;//[10, 20, 50, 50, 60]
System.out.println(list);
int i = Collections.binarySearch(list, 50);
System.out.println(i);//2
int max = Collections.max(list);
System.out.println(max);//60
Collections.reverse(list);
System.out.println(list);//[60, 50, 50, 20, 10]
Collections.shuffle(list);
System.out.println(list);//[10, 60, 50, 20, 50] [50, 10, 60, 20, 50]
Collections.addAll(list, 5,6,70);
System.out.println(list);//[50, 10, 50, 60, 20, 5, 6, 70]
Collections.copy(list, list2);//list2的size要小于list的size
System.out.println(list);//[100, 50, 30, 50, 10, 5, 6, 70]