说到Java集合,可以说是初学者必踩的坑了。
那么怎么才能学好Java集合呢?个人认为关键是要理清楚思路,从上而下,建立一套完整的思维体系,这样才能更好的去认识事物的本质。
先确立一下学习Java集合的目标:
1.会使用集合存储数据。
2.会遍历集合,把数据取出来。
3.掌握每种集合的特性。
总的集合框架如图所示:
Collection<E>接口
首先从Collectiion 讲起 ,Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些操作方法可以用于操作多有的单列集合(所有子类都可以使用)。
方法如下(重点):
* public boolean add(E e): 把给定的对象添加到当前集合中。
* public boolean clear():清空集合中所有的元素。
* public boolean remove(E e):把给定的对象在当前集合中删除。
* public int size():输出集合中元素个数。
* public bollean contains(E e):判断当前集合是否包含指定对象。
* public boolean isEmpty():判断当前集合是否为空。
* public Object [ ] toArray:把集合中的元素储存在数组中
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class DemoCollection{ public static void main(String[] args){ //创建集合对象,使用多态。 Collection<String> coll = new ArrayList<>();//ArrayList是Collection的实现类 System.out.println(coll); coll.add("寒冰射手"); coll.add("无双剑姬"); coll.add("满族之王"); System.out.println(coll); boolean b1= coll.remove("寒冰射手");//添加对象 System.out.println("b1是否移除:"+b1); boolean b2 = coll.contains("无双剑姬");//判断对象是否存在集合当中 System.out.println("b2是否包含:"+b2); int size = coll.size();//返回集合的大小 System.out.println("size:"+size); if(coll.isEmpty()){ //判断集合是否为空 System.out.println("集合为空"); }else{ Object[] arr = coll.toArray();//把集合中的元素存储在数组当中 for(int i = 0;i<arr.length;i++){ System.out.println("arr[i]");//输出数组中的元素 } } //使用迭代器遍历集合 Iterator<String> it = coll.iterator(); while(it.hasNext()){ String str = it.next(); System.out.println(str); } //coll.clear();清空集合当中的元素 } }
List 接口(java.util.List)
List接口继承自Collection接口,一般将List接口的对象称为List集合。List集合中重要是允许出现重复的元素,所有的元素以线性的方式存储。
List接口的特点:
1.有序的集合,存储元素和储存元素的顺序是一致的。
2.有索引,包含了一些带索引的方法(Collection抽取所有子接口的共性,所以不带索引)。
3.允许存储重复的元素。
List接口的常用方法(带索引 特有):
* public void add(int index,E element):将指定元素,添加在集合中的指定位置上。
* public E get (int index):返回集合中指定位置的元素。
* public E remove(int index):返回移除集合指定位置的元素。
* public E set(int index):返回替换集合中指定位置的元素。
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class DemoSet { public static void main(String[] args) { List<String> list = new ArrayList<>();//创建List集合对象,使用多态 list.add("a"); list.add("b"); list.add("c"); list.add("d"); System.out.println(list); list.add(2,"f");//指定位置添加元素 System.out.println(list); list.remove(3);//移除指定位置元素 System.out.println(list); String str = list.get(1);//获取指定位置元素 System.out.println(str); list.set(0,"p");//替换指定位置元素 System.out.println(list); Iterator<String> it = list.iterator();//使用迭代器顺序取出元素, while(it.hasNext()){ String str1 = it.next(); System.out.println(str1); } } }
ArrayList集合
ArrayList为List接口的实现类,底层储存为数组结构,用于元素的增删改查,属于最常用的集合(不同步)
常用方法:
* public boolea add(E e);
* public E get(int index);
* public E remove(int index);
* public int size();
import java.util.ArrayList; import java.util.Iterator; public class DemoSet { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); System.out.println(list); //注意,for循环遍历,长度用list.size for (int i = 0; i < list.size(); i++) { String str = list.get(i); System.out.println(str); } //迭代器遍历 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str1 = it.next(); System.out.println(str1); } } }
LinkedList集合
LinkedList也为List接口的实现类,但其与ArrayList集合不同的是其底层数据存储的结构为链表结构(双向),包含大量操作首位元素的方法,查询慢,增删快。
常用方法(特有方法,不能使用多态):
* public void addFirst(E e):将指定元素插入此列表开头。
* public void addLast(E e):将指定元素插入此列表的末尾。
* public E getFirst():返回此列表的第一个元素。
* public E getLast():返回此列表的最后一个元素。
* public E removeFirst():移除并返回此列表第一个元素。
* public E removeLast():移除并返回此列表的最后一个元素。
* public E pop():从此列表所表示的堆栈处弹出一个元素。(等效与removeFirst()方法)
* public void push(E e):将元素推入此列表所表示的堆栈。(等效于add()方法)
* public boolean isEmpty():如果列表不包含元素,则返回true。
import java.util.Iterator; import java.util.LinkedList; public class DemoSet { public static void main(String[] args) { show1(); } public static void show1(){ LinkedList<String> list = new LinkedList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.push("f"); System.out.println(list); String f1 = list.getFirst(); System.out.println(f1); String l1 = list.getLast(); System.out.println(l1); String s1 = list.removeFirst();//等效pop()方法 System.out.println(s1); String s2 = list.removeLast(); System.out.println(s2); //list.clear(); 清空集中的元素,在获取集合中元素时会抛出NoSuchElementException //使用迭代器遍历Linked中的元素 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); System.out.println(str); } } }
Vector集合
Vector也继承自List接口,底层也是一个实现可增长的对象数组,也可以通过索引来进行访问,但一般Vector使用较少,所以不在这进行详细讲解。
一般方法(特有):
* public void addElement(E e):添加元素到向量末尾。(等效于add()方法)
* public Enumeration<E> elements():返回该向量枚举。
Set接口
Set接口是Collection接口下第二大分支,也继承Collection接口。
Set接口特点:
1.不允许存储相同的元素
2.没有索引,没有带索引的方法,也不能使用普通的for循环。(一般来说,有索引就可以使用for循环来遍历)
Set方法不在详细描述,基本与Collection接口方法一致。
HashSet
HashSet是Set接口的实现类,由哈希表支持(HashMap实例),它不保证set的迭代顺序,即存储元素和取出元素的顺序有可能不一致。
import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class DemoSet { public static void main(String[] args) { Set<Integer> set = new HashSet<>(); set.add(1); set.add(2); set.add(3); set.add(1); Iterator<Integer> it = set.iterator(); while(it.hasNext()){ int a = it.next(); System.out.println(a); } } }
关于Set集合不允许重复元素的原理:
Set集合在调用add方法时,add()方法会调用元素的hashCode方法和equals方法判断元素是否重复。
前提:存储的元素必须重写hashCode和equals方法。(如用HashSet存储自定义对象,必须重写equals和hashCode方法)
说一下hashCode()方法 :返回一个int型的哈希值(一串数字)。
import java.util.HashSet; public class DemoSet { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); String s1 = new String("abc"); String s2 = new String("abc"); set.add(s1); set.add(s2); set.add("你好"); set.add("我好"); set.add("abc"); System.out.println(set); } }
set.add(s1): add方法会调用s1的hashCode方法,计算“abc”的哈希值,在集合中找有没有和“abc”哈希值一样的元素,发现没有就会把s1储存在集合当中。
set.add(s2):同上操作,发现有哈希冲突后,就会调用s2的equals方法去比较s2.equals(s1),equals返回true,认定两个元素相同,就不会把s2储存在集合当中。
set.add:("你好"):同s1操作相同。
set.add("我好"):哈希值与“你好”相同,但是equals方法返回false,认定“你好”,“我好”不同,就会把“我好”元素挂在“你好”下面,放入集合。
hashSet集合的 底层 哈希表结构 :数组+链表/红黑树。
数组 | 哈希值1 | 哈希值2 |
链表 | 元素1 | 元素3 |
链表 | 元素2 | 元素4 |
LinkedHashSet集合
特点:继承了HashSet集合,其底层是一个哈希表+链表,多了一条链表(记录元素的存储顺序),保证元素有序。
具体方方不再详述,与HashSet相同。
Collections集合工具类:
对集合进行操作,部分方法如下:
* public static <T> boolean addAll(Collection<T> c,T..elements):往集合中添加多个元素。
* public static void shuffle(List<?> List):打乱集合中元素的顺序 。
* public static <T> void sort(List<T> list):将集合中元素按默认规则排序元素。(升序)
*注意:如果是自定义类型,使用sort()需要重写compaerTo()方法.
* public static <T> void sort(List<T>,Comparator<?super T>) :将集合中元素按指定规则排序。
例如:Collections.addAll(list,"a","b".....)(静态方法调用,直接类名.方法)。
Map<K,V>集合(有点懒,包括其子类就放一块讲完啦)
<key,value>双列集合,通多键值对方式组成。通过建可以找到其值。
Map集合的特点:
1.Map集合是一个双列集合,一个元素包含两个值(key,value)。
2.Map集合中的元素,key和value的数据类型可以相同,也可以不同。
3.Map集合中的元素,键是不允许重复的,但是值是可以重复的。
常用方法:
* public V put(K key,V value):把指定的键与值添加到Map集合当中。(存储键值对时,键相同,值不同,后添加的值会把原来的值覆盖)
* public V remove (Object key):把指定的键所对应的值对元素在Map集合中删除,返回被删除元素的值。
* public V get(Obejec key):获取键所对应的值
* boolean containsKey(Object key):判断集合中是否包含指定的键。
* public Set<K> keySet():获取Map集合中的所有键,并把它放入Set集合。(重要)
* public Set<Map.Entry<K,V>> entrySet():把键值对的对象(Entry)放入Set集合中。(重要)
import java.util.*; public class DemoSet { public static void main(String[] args) { Map<String,String> map = new HashMap<>(); map.put("a","a"); map.put("b","b"); map.put("c","c"); map.put("d","d"); map.put("a","f"); System.out.println(map); String str1 = map.get("a"); System.out.println(str1); String str2 = map.remove("c"); System.out.println(str2); System.out.println(map); boolean flag = map.containsKey("c"); System.out.println("键C是否存在:"+flag); //遍历键值对,注意和之前的集合遍历不太一样 Set<String> set = map.keySet(); Iterator<String> it = set.iterator(); while(it.hasNext()){ String key = it.next(); String value = map.get(key); System.out.println(key+"="+value); } System.out.println("------------------------------------");//华丽的分割线 Map.Entry为其Map内置接口 //使用entrySet遍历 Set<Map.Entry<String, String>> entry = map.entrySet(); Iterator<Map.Entry<String, String>> iterator = entry.iterator(); while(iterator.hasNext()){ Map.Entry<String, String> next = iterator.next(); System.out.println(next); } System.out.println("------------------------------------");//华丽的分割线 //也可使用增强for循环遍历 for(Map.Entry<String, String> s1:map.entrySet()){ System.out.println(s1); } } }
*注意:Map.Entry<k,v>:在Map接口中由一个内部接口Entry,当Map集合一创建,就会创建一个Entry对象,用来记录键值对。
可以把Map.Entry<K,V>,理解成一种引用类型,在增强for循环中去遍历使用,用来接收传来的键值对。
HashMap集合
HashMap<k,v>集合为Map<k,v>接口的实现类,其底层为哈希表。为无顺序集合的集合,即存取顺序不一致,线程不安全,不同步,多线程。
Hashmap存储自定义类型键,自定义类型的键就要重写hashcode方法和equals方法,保证键的唯一性。值可以重复。(具体不详述)
LinkedHashMap集合
LinkedHashMap是HashMap的子类。其底层是哈希表+链表(保证迭代顺序),它是一个有序的集合,存取顺序一致。
Hashtable集合
Hashtable为Map接口的实现类,其底层也为哈希表,同步且为单线程集合,不常用。Hashtable 不能存取null值。
如果在Hashtable集合中存放null,则会抛出NullPointerException异常。
但是Hashtable的子类Properties集合依然活跃,Properties是唯一和IO流相结合的集合。