集合
集合的概述
Java中集合是一种常用的数据结构,例如:队列、栈、链表等。
集合市一中容器,用于存储数量不等的对象,并按照规范实现一些常用的操作和算法。在使用集合时,不必考虑数据结构和算法的具体实现细节,根据需要直接使用这些集合类并调用相应的方法即可,从而提高了开发效率
Java的集合类主要由两个接口派生而出,分别是Collection和Map派生出的子接口或实现类。
Java的常用集合主要分为三大类:
Set集合:将对象添加到Set集合中,Set集合无法记住添加的顺序,因此Set集合中的元素不能重复,否则就无法识别改元素,访问Set集合中的元素也只能根据元素本身进行访问;
List集合:与数组相似,List集合可以记住每次添加元素的顺序,因此可以根据元素的索引访问List集合中的元素,List集合中的元素可以重复而且长度是可变的;
Map集合:每个元素都是由key/value键值对组成,可以根据每个元素的key来访问对应的value,Map几种的key不允许重复,value可以重复;
Collection接口的类图:
迭代器接口(Iterator)
迭代器采用统一的方式对Collection集合中的元素进行遍历操作,开发人员无需关心Collection集合中的类容,直接使用foreach循环遍历集合中的部分或全部元素。
使用Collection集合需要注意的问题:
add()、addAll()、remove()、removeAll()和retain()方法可能会引发不支持该操作的UnsupportedOperationException异常;
将一个不兼容的对象添加到集合中时,将产生ClassCastException异常;
Collection接口没有提供获取某个元素的方法,但可以通过iterator()方法获取迭代器来遍历集合中的所有元素;
虽然Collection中可以存储任何的Object对象,但不建议同一个集合容器中存储不同类型的对象,建议使用泛型增强集合的安全性,以免引起ClassCastException异常;
集合类
1.List接口
List集合是按照元素的添加顺序来设置元素的索引的,索引从0开始,例如:第一次添加的元素的索引为0,第二次添加的元素的索引为1,第n次添加元素的索引为n-1。当使用无效索引时将产生IndexOutBoundsException异常。
ArrayList和Vector是List接口的两个典型实现类,完全支持List接口中的所有功能方法。ArrayList称为“数组列表”,而Vector称为“向量”,两者都是基于数组实现的列表集合,但该数组是一个动态的、长度可变的、并允许再分配的Object[]数组。
ArrayList类与Vector类:
public class ArrayListTest {
public static void main(String[] args) {
//定义ArrayList集合,并指明集合中存储的数据类型
ArrayList<String> list = new ArrayList<String>();
list.add("Hello");//向list中添加数据
list.add("Word");
list.add("Java");
//如果ArrayList没有指定类型,这样写不报错,但后期对这个数据的取用,则会增加难度
//list.add(15);
System.out.println("-----------foreach--------------");
//使用 foreach 遍历集合
for (String string : list) {
System.out.println(string);
}
System.out.println("-------------i----------------");
list.remove(1); //通过下标移除指定下标的元素
for (int i = 0; i < list.size(); i++) {//直接遍历集合
System.out.println(list.get(i));
}
System.out.println("-------------iterator----------------");
list.remove("Hello");//通过值移除对应的元素
//获取List集合中的Iterator
Iterator<String> iterator = list.iterator();
//判断是否有下一个元素
while (iterator.hasNext()) {
//将下一个元素输出来
System.out.println(iterator.next());
}
}
}
/**
* Vector的用法与ArrayList完全一致,
* 只不过Vector加入了线程安全,它里面的方法是同步的,
* 这样使Vector效率降低
*
* ArrayList虽然线程不安全,即线程不同步,但效率高
*/
Stack类
Stact类是Vector的子类,它具有栈的性质:
public class StackTest {
public static void main(String[] args) {
Stack<String> stack = new Stack<String>();
// 依次向Stack中添加元素
stack.add("Hello");
stack.add("Word");
stack.add("Java");
// 输出Stack中的元素情况,调用Stack中的toString()方法
System.out.println(stack);
while (!stack.empty()) {
// pop()方法是Stack中的元素弹出方法,用于依次取Stack中的元素
System.out.println(stack.pop());
}
}
}
2.Set接口
Set集合不能记住元素添加的顺序,因此不能包含相同的元素。
Set接口常用的实现类包括HashSet、TreeSet和EnumSet,这三个实现类各具特色:
HashSet是Set接口的典型实现类,大多数使用Set集合时都使用该实现类。HashSet使用Hash算法来存储集合中的元素,具有良好的存、取和查找的特性;
TreeSet采用Tree“树”的数据结构来存储集合元素,因此可以保证集合中的元素处于排序状态。TreeSet支持两种排序方式:自然排序和定制排序,默认情况下采用自然排序;
public class SetTest { public static void main(String[] args) { // 定义一个HashSet HashSet<String> hashSet = new HashSet<String>(); // 向HashSet中添加一组顺序元素 hashSet.add("第1");// 加入了两个相同的元素,另一个元素将不会在集合中 hashSet.add("第1"); hashSet.add("第2"); hashSet.add("第3"); hashSet.add("第4"); /** * Set集合是无序的,因此Set集合只能用foreach或Iterator遍历 * */ System.out.println("--------hashSet--foreach--------"); for (String string : hashSet) { System.out.println(string); } hashSet.remove("第1");// 移除一个元素 System.out.println("--------hashSet--Iterator--------"); Iterator<String> hash = hashSet.iterator(); while (hash.hasNext()) { System.out.println(hash.next()); } System.out.println(); // 定义一个TreeSet TreeSet<String> treeSet = new TreeSet<String>(); // 向TreeSet中添加一组无序元素 treeSet.add("第1");// 加入了两个相同的元素,另一个元素将不会在集合中 treeSet.add("第1"); treeSet.add("第3"); treeSet.add("第4"); treeSet.add("第2"); /** * TreeSet集合默认会对元素进行自然排序, * 当然排过序的集合也是没有下标位置的 * */ System.out.println("--------treeSet--foreach--------"); for (String string : treeSet) { System.out.println(string); } treeSet.remove("第1");// 移除一个元素 System.out.println("--------treeSet--Iterator--------"); Iterator<String> tree = treeSet.iterator(); while (tree.hasNext()) { System.out.println(tree.next()); } } }
3.Queue接口
Queue接口(即队列)常用“先进先出”的方式排序各个元素,即最先入队的元素最先出队。它继承Collection接口,除了拥有Collection的基本操作外,它还提供队列的插入、提取和检查操作,且每个操作都存在两种形式:一种操作失败时抛出异常;另一种操作失败时返回一个特殊值(null或false)。
Deque接口(双端队列)是Queue接口的子接口,支持在队列的两端插入和移除元素。
链接列表LinkedList是Deque和List两个接口的实现类,兼具队列和列表两种特性,是最常使用的集合之一。LinkedList不是基于线程安全,如果多个线程同时访问一个LinkedList实例,而其中至少有一个线程从结构上修改列表时,则必须有外部代码手动保持同步。
ArrayDeque称为“数组双端队列”,是Deque接口的实现类。 不常用
1、ArrayDeque没有容量限制,可以根据需要增加容量。
2、ArrayDeque不是基于线程安全的,不支持多线程的并发访问。
3、ArrayDeque禁止添加null元素。
4、ArrayDeque在用作堆栈时,快于Stack;用作队列时,快于LinkedList。PriorityQueue是Queue接口的实现类,是基于优先级的、无界队列,通常称为“优先级队列”。优先级队列的元素按照其自然顺序进行排序,或制定排序,优先级队列不允许使用null元素。依靠自然顺序的优先级队列不允许插入不可比较的对象。不常用
public class LinkedListTest { public static void main(String[] args) { //创建LinkedList的对象 LinkedList<Integer> linkedList = new LinkedList<Integer>(); //给LinkedList集合添加5个元素 for (int i = 0; i < 5; i++) { linkedList.add(i+1); } System.out.println("---------输出5个元素----------"); System.out.println(linkedList); linkedList.offer(6);//在集合的末尾添加一个元素 linkedList.offerFirst(0);//在集合的开头添加一个元 System.out.println("-------------------------"); System.out.println(linkedList); System.out.println("\n-------用下标方式遍历集合-------"); for (int i = 0; i < linkedList.size(); i++) { System.out.print(linkedList.get(i) + ", "); } System.out.println("\n-------查看集合的第一个元素---------"); System.out.println(linkedList.peekFirst()); System.out.println(linkedList); System.out.println("-------推出集合中的第一个元素----------"); System.out.println(linkedList.pollFirst()); System.out.println(linkedList); System.out.println("--------弹出集合中的第一个元素---------"); System.out.println(linkedList.pop()); System.out.println(linkedList); } }
4.Map接口
Map接口是集合框架的另一个根接口,与Collection接口并列。Map是以key/value键值对映射关系存储集合,其key-value键值对映射关系如图:
HashMap和TreeMap是Map体系中两个常用实现类,其特点如下:
HashMap是基于哈希算法的Map接口的实现类,该实现类提供所有映射操作,并允许使用null键和null值,但不能保证映射的顺序,即是无序的映射集合;
TreeMap是基于“树”结构来存储的Map接口实现类,可以根据其键的自然顺序进行排序,或定制排序方式。
public class MapTest { public static void main(String[] args) { //定义一个HashMap HashMap<String,String> hashMap = new HashMap<String, String>(); hashMap.put("a", "张三");//向HashMap集合中添加元素 hashMap.put("b", "李四"); hashMap.put("c", "王五"); hashMap.put(null, "not");//可以添加键或者值为空的元素 hashMap.remove("a");//将键为“a”的元素移除 //通过其键 获取值, System.out.println(hashMap.get("a"));//键为“a”的元素被移除,输出的值为null System.out.println(hashMap.get("b")); System.out.println(hashMap.get("c")); System.out.println(hashMap.get(null)); //定义一个TreeMap TreeMap<String, String> treeMap = new TreeMap<String, String>(); treeMap.put("1", "zhangsan");//与HashMap一样,添加元素 treeMap.put("2", "lisi"); //treeMap.put(null,null);//不允许添加空值,这样写会报错 //通过其键获取值,不允许用null去获取 System.out.println(treeMap.get("1")); } }
3.集合转换
在Java中,集合有两大类,分别是Collection接口下List、Set、Queue等集合和Map接口下的HashMap、TreeMap等。将Map集合转换成Collection集合,例如:
public class MapToCollection {
public static void main(String[] args) {
// 定义一个HashMap
HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("a", "张三");// 向HashMap集合中添加元素
hashMap.put("b", "李四");
hashMap.put("c", "王五");
//将Map对象转换成Collection对象
//Set是Collection的子类,
//通过HashMap中的entry()方法可以将Hash对象转换成Set对象
Set<Entry<String,String>> entrySet = hashMap.entrySet();
//用foreach 将Set集合中的Entry输出
for (Entry entry : entrySet) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
//将HashMap的键通过keySet()方法转换成Set对象,依次输出
Set<String> keySet = hashMap.keySet();
for (String string : keySet) {
System.out.println(string);
}
//将HashMap的值通过values()方法转换成Collection对象,依次输出
Collection<String> values = hashMap.values();
for (String string : values) {
System.out.println(values);
}
}
}
4.集合工具类
Java集合框架中还提供了两个非常实用的辅助工具类:Collections和Arrays。用法如下:
Collections工具类
提供了一些对Collection集合常用的静态方法,如:排序、复制、查找、以及填充等操作。
使用Collections工具类为集合进行排序时,集合中的元素必须是Comparable可比较的。即实现Comparable接口和Comparable接口中的compareTo()方法
//实现Comparable接口
public class Fruit implements Comparable<Fruit>{
protected String name;
protected float price;
public Fruit(String name, float price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Fruit [name=" + name + ", price=" + price + "$]";
}
//实现Comparable接口中的compareTo()方法
@Override
public int compareTo(Fruit fruit) {
if (this.price < fruit.price) {
return -1;
} else if (this.price == fruit.price) {
return 0;
} else {
return 1;
}
}
}
public class Test {
public static void main(String[] args) {
List<Fruit> list = new ArrayList<Fruit>();
list.add(new Fruit("Apple", 2.5f));
list.add(new Fruit("Banana", 3.5f));
list.add(new Fruit("Grape", 2.0f));
list.add(new Fruit("Orange", 1.5f));
System.out.println("---------排序前------------");
for (Fruit fruit : list) {
System.out.println(fruit);
}
/**
* 通过Collections辅助类对集合进行排序
* 排序的集合中的类需实现Comparable接口
* 重写compareTo()方法,按照什么方式排序
* */
Collections.sort(list);
System.out.println("---------排序后------------");
for (Fruit fruit : list) {
System.out.println(fruit);
}
System.out.println("---取排序后集合中的最大、最小值---");
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
}
}
Arrays工具类
Arrays工具类提供了针对数组的各种静态方法,如:排序、复制、查找等操作。
public class Test {
public static void main(String[] args) {
//创建一个Fruit数组
Fruit[] fruits = new Fruit[4];
//实例化
fruits[0] = new Fruit("Apple", 2.5f);
fruits[1] = new Fruit("Banana", 3.5f);
fruits[2] = new Fruit("Grape", 2.0f);
fruits[3] = new Fruit("Orange", 1.5f);
System.out.println("---------排序前------------");
for (Fruit fruit : fruits) {
System.out.println(fruit);
}
//使用Arrays工具类进行排序,Fruit类也要实现Comparable接口才能进行排序
Arrays.sort(fruits);
System.out.println("---------排序后------------");
for (Fruit fruit : fruits) {
System.out.println(fruit);
}
System.out.println("----将Fruit数组转换成字符串-----");
System.out.println(Arrays.toString(fruits));
System.out.println("----将Fruit数组拷贝到一个新数组中-----");
Fruit[] otherFruits = Arrays.copyOfRange(fruits, 3, 4);
for (Fruit fruit : otherFruits) {
System.out.println(fruit);
}
}
}