如果对象的数量与生命周期都是固定的,自然我们也就不需要很复杂的数据结构。
我们可以通过创建引用来持有对象,如
Class clazz;
也可以通过数组来持有多个对象,如
Class[] clazs = new Class[10];
然而,一般情况下,我们并不知道要创建多少对象,或者以何种方式创建对象。数组显然只能创建固定长度的对象,为了使程序变得更加灵活与高效,Java类库提供了一套完整的容器类,具备完善的方法来解决上述问题。
从上图可以看到容器中有七大接口:
其中List, Queue和Set接口继承了Collection接口,剩下的接口之间都是相互独立的,无继承关系。Iterater迭代器则是为了更灵活的迭代集合,与foreach一起使用。Comparable接口则用于比较。
除了接口之外,容器中还包含了List接口的实现类LinkedList,Set接口的实现类HashSet等;容器中还包含两个工具类/帮助类:Collections和Arrays,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
容器主要分为两种类型:
在java.util包中的Collections和Arrays类中包含了很多使用方法,可以再一个Collection中添加一组元素。
如下演示一个例子:
///添加一组元素 import java.util.*; public class AddingGoups { public static void main(String[] args) { //方式一 List<Integer> list = Arrays.asList(1,2,3,4,5); list.set(1, 1); //list.add(21); 无法改变长度,因为Arrays.asList()的输出,其底层表示的是数组 Collection<Integer> collection = new ArrayList<Integer>(list); //方式二 Integer[] moreInts = {6,7,8,9,10}; Collections.addAll(collection,moreInts); Collections.addAll(collection,11,12,13,14,15); //输出 System.out.println(collection); } }
输出结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
注意:可以直接使用Arrays.asList()的输出,将其作为List,但是这种情况下,其底层表示的是数组,因此不能调整尺寸。所以使用add()、delete()会出现错误。此外Arrays.asList()它对其所产生的List类型如做出了最理想的假设,即由传入的元素类型所决定,如List<Integer> list = Arrays.asList(1,2,3,4,5),因此如果将其赋值给不同的List类型可能会引发问题。
容器的输出,默认调用的是容器.toString()方法,如下:
///打印容器 import java.util.*; public class PrintingContainers { static Collection fill(Collection<String> collection) { collection.add("rat"); collection.add("cat"); collection.add("dog"); collection.add("pig"); return collection; } static Map fill(Map<String,String> map) { map.put("rat","Fuzzy"); map.put("cat","Rags"); map.put("dog","Bosco"); map.put("pig","Spot"); return map; } public static void main(String[] args) { System.out.println(fill(new ArrayList<String>())); System.out.println(fill(new LinkedList<String>())); System.out.println(fill(new HashSet<String>())); System.out.println(fill(new TreeSet<String>())); System.out.println(fill(new LinkedHashSet<String>())); System.out.println(fill(new HashMap<String,String>())); System.out.println(fill(new TreeMap<String,String>())); System.out.println(fill(new LinkedHashMap<String,String>())); } }
输出如下:
[rat, cat, dog, pig] [rat, cat, dog, pig] [rat, cat, dog, pig] [cat, dog, pig, rat] [rat, cat, dog, pig] {rat=Fuzzy, cat=Rags, dog=Bosco, pig=Spot} {cat=Rags, dog=Bosco, pig=Spot, rat=Fuzzy} {rat=Fuzzy, cat=Rags, dog=Bosco, pig=Spot}
相比于Collection接口,添加了新的方法,使得可以再List中间插入和移除元素。其实现类有以下两个:
下面演示一个ArrayList的例子,将ArrayList向上转型为List:
import java.util.*; class Person{ public String name; public int age; public boolean sex; public Person(String name, int age, boolean sex) { super(); this.name = name; this.age = age; this.sex = sex; } public String toString(){ return name + "-" + age + "-" + sex; } } public class ListFeatures { public static void main(String[] args) { List<Person> pers = new ArrayList<Person>(); Person p1 = new Person("吴定会",50,true); pers.add(p1); Person p2 = new Person("沈艳霞",46,false); pers.add(p2); Person p3 = new Person("张三",17,true); pers.add(p3); Person p4 = new Person("李四",34,false); pers.add(p4); System.out.println("1:"+pers); //成员函数1 System.out.println("2:"+pers.contains(p1)); //成员函数2 Person p = pers.get(2); //成员函数3 System.out.println("3:" + p +" " + pers.indexOf(p)); //成员函数4 System.out.println("4:" + pers.remove(p)); System.out.println("5:"+pers); //成员函数5 List<Person> sub = pers.subList(0, 2); //不包含2 System.out.println("6:"+sub); //成员函数6 System.out.println("7:"+pers.containsAll(sub)); Collections.shuffle(sub); System.out.println("shuffled sublist:" + sub); //成员函数7 pers.removeAll(sub); System.out.println("8:"+pers); //成员函数8 System.out.println("9:"+pers.isEmpty()); } }
输出如下:
1:[吴定会-50-true, 沈艳霞-46-false, 张三-17-true, 李四-34-false] 2:true 3:张三-17-true 2 4:true 5:[吴定会-50-true, 沈艳霞-46-false, 李四-34-false] 6:[吴定会-50-true, 沈艳霞-46-false] 7:true shuffled sublist:[沈艳霞-46-false, 吴定会-50-true] 8:[李四-34-false] 9:false
在上面例子中我们使用了List的部分方法:
除了以上方法,List还有很多方法,比如set()、replace()、clear()、addAll()、toArray()等。
下面演示一个LinkedList的例子
import java.util.*; public class LinkedListFeatures { public static void main(String[] args) { LinkedList<Person> pers = new LinkedList<Person>(); Person p1 = new Person("吴定会",50,true); pers.add(p1); Person p2 = new Person("沈艳霞",46,false); pers.add(p2); Person p3 = new Person("张三",17,true); pers.add(p3); Person p4 = new Person("李四",34,false); pers.add(p4); System.out.println(pers); //成员函数1 返回第一个元素 System.out.println("getFirst():"+pers.getFirst()); //成员函数2 返回第一个元素 System.out.println("element():" + pers.element()); //成员函数3 返回第一个元素 System.out.println("peek():" + pers.peek()); //成员函数4 移除并返回第一个元素 System.out.println("remove():" + pers.remove()); //成员函数5 删除并返回第一个元素 System.out.println("removeFirst():" + pers.removeFirst()); //成员函数6 删除并返回第一个元素 System.out.println("poll():" + pers.poll()); System.out.println(pers); //成员函数7 追加元素到List头部 pers.addFirst(new Person("郑洋",25,true)); System.out.println("After addFirst():" + pers); //成员函数8 追加元素到List尾部 pers.offer(new Person("杨德亮",25,true)); System.out.println("After offer():" + pers); //成员函数9 追加元素到List尾部 pers.addLast(new Person("黄旭",25,true)); System.out.println("After addLast():" + pers); //成员函数10 删除并返回最后一个元素 System.out.println("After removeLast():" + pers.removeLast()); } }
输出:
[吴定会-50-true, 沈艳霞-46-false, 张三-17-true, 李四-34-false] getFirst():吴定会-50-true element():吴定会-50-true peek():吴定会-50-true remove():吴定会-50-true removeFirst():沈艳霞-46-false poll():张三-17-true [李四-34-false] After addFirst():[郑洋-25-true, 李四-34-false] After offer():[郑洋-25-true, 李四-34-false, 杨德亮-25-true] After addLast():[郑洋-25-true, 李四-34-false, 杨德亮-25-true, 黄旭-25-true] After removeLast():黄旭-25-true
注意:上面程序中的element()、offer()、peek()、poll()、等方法都是Queue接口的方法,具体内容在Queue接口中将会详细介绍。
Set不保存重复的元素,如果试图将相同对象的多个实例添加到Set中,那么它会组织这种重复现象。Set可以很容易查询某个元素是否在某个Set中,正因为如此,查找就成为了Set中最重要的操作。
具有与Collection相同的方法。实际上Set就是Collection,只是行为不同。其实现类有以下两个:
此外还有一个类型LinkedHashSet,继承自HashSet:
下面是使用存放Integer对象的HashSet的示例:
import java.util.*; public class SetOfInteger { public static void main(String[] args) { Random rand = new Random(47); Set<Integer> intset = new HashSet<Integer>(); for(int i=0;i<10000;i++) { intset.add(rand.nextInt(30)); } System.out.println(intset); } }
输出结果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
在本例中,intset中插入10000次,但是由于不保存重复元素,最终输出结果数目<=30。
如果想对插入的元素进行排序,可以使用TreeSet。
import java.util.*; public class SortedSetOfInteger { public static void main(String[] args) { Random rand = new Random(47); Set<Integer> intset = new TreeSet<Integer>(); for(int i=0;i<10000;i++) { intset.add(rand.nextInt(30)); } System.out.println(intset); } }
输出如下:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
队列是一个典型的先进先出(FIFO)的容器,即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要。
Queue的实现有以下两个:
下面的示例使用了Queue接口中与Queue相关的方法:
import java.util.*; public class QueueDemo { public static void printQ(Queue queue){ while(queue.peek() != null) { System.out.print(queue.remove() + " "); } System.out.println(); } public static void main(String[] args) { Queue<Integer> queue = new LinkedList<Integer>(); Random rand = new Random(47); for(int i=0;i<10;i++) { queue.offer(rand.nextInt(i+10)); } printQ(queue); Queue<Character> qc = new LinkedList<Character>(); for(char c:"LaJiShiPin".toCharArray()) { qc.offer(c); } printQ(qc); } }
输出如下:
8 1 1 1 5 14 3 1 0 1 L a J i S h i P i n
下面介绍一下上面用到的方法:
自动装包机制会自动将nextInt()方法的int结果转换为Integer对象,将char c转换成qc所需的Character对象。Queue接口窄化了对LinkedList的方法的访问权限,以使得只有恰当的方法可以使用,因此能够访问的LinkedList方法会变少(这里我们可以将queue转换回LinkedList,但是最好不要这么做)。
先进先出描述的是最典型的队列规则。队列规则是指在给定一组队列中的所有元素的情况下,确定下一个弹出队列的元素的规则。先进先出声明的是下一个元素应该等待时间最长的元素。
优先级队列声明下一个弹出元素是最重要的元素(具有最高的优先级)。例如,在飞机场,当飞机临近起飞时,这架飞机的乘客可以再办理登记手续时排到队头。
当调用PriorityQueue中的offer()方法:会插入一个对象到队列中,并且这个对象会在队列中被排序。
PriorityQueue可以确保调用peek(),poll()和remove()方法时,获取的元素将是队列中优先级最高的元素。
下面演示一个示例:
import java.util.*; public class PriorityQueueDemo { public static void main(String[] args) { //默认从小到大排序 PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>(); Random rand = new Random(47); for(int i=0;i<10;i++) { //将一个元素插入到队尾 priorityQueue.offer(rand.nextInt(i+10)); } QueueDemo.printQ(priorityQueue); //默认从小到大排序 List<Integer> ints = Arrays.asList(25,22,20,18,14,9,3,1,1,2,3,9,14,18,21,23,25); priorityQueue = new PriorityQueue<Integer>(ints); QueueDemo.printQ(priorityQueue); //从大到小排序 priorityQueue = new PriorityQueue<Integer>(ints.size(),Collections.reverseOrder()); priorityQueue.addAll(ints); QueueDemo.printQ(priorityQueue); //按字母从小到大排序 String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION"; List<String> strings = Arrays.asList(fact.split(" ")); PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings); QueueDemo.printQ(stringPQ); //按字母从大到小排序 stringPQ = new PriorityQueue<String>(strings.size(),Collections.reverseOrder()); stringPQ.addAll(strings); QueueDemo.printQ(stringPQ); //按字母从小到大排序 Set<Character> charSet = new HashSet<Character>(); for(char c:fact.toCharArray()) { charSet.add(c); } PriorityQueue<Character> characterPQ = new PriorityQueue<Character>(charSet); QueueDemo.printQ(characterPQ); } }
输出如下:
0 1 1 1 1 1 3 5 8 14 1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25 25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1 EDUCATION ESCHEW OBFUSCATION SHOULD SHOULD OBFUSCATION ESCHEW EDUCATION A B C D E F H I L N O S T U W
从输出结果可以看到,重复是允许的,在默认排序规则下最小的值拥有最高的优先级。此外,还演示了如何使用自己的Comparator对象来改变排序,我们在PriorityQueue<类型>构造器调用的时候使用了由Collections.reverseOrder()产生的反序的Comparator。
Integer、String、Character可以与PriorityQueu一起工作,因为这些类已经内建了自然排序,如果想在PriorityQueu中使用自己的类,就必须包含额外的功能以产生自然排序,或者提供自己的Comparator。
Map的实现有以下两个:
此外还有一个类型LinkedHashMap,继承自HashMap:
下面演示一个HashMap的例子:
import java.util.*; public class HashMapDemo { public static void main(String[] args) { Random rand = new Random(47); Map<Integer,Integer> m = new HashMap<Integer,Integer>(); for(int i=0;i<10000;i++) { int r = rand.nextInt(20); //获取出现次数 如果不存在返回null Integer freq = m.get(r); m.put(r, freq == null?1:freq + 1); } System.out.println(m); } }
输出如下:
{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 16=533, 17=509, 18=478, 19=464}
下面的示例将使用String描述来查Person:,还通过containsKey()和containsValue()来测试一个Map,以便看它是否包含某个键或某个值:
import java.util.*; public class PersonHashMap { public static void main(String[] args) { Map<String,Person> perMap = new HashMap<String,Person>(); perMap.put("吴定会", new Person("吴定会",50,true)); perMap.put("沈艳霞", new Person("沈艳霞",46,false)); System.out.println(perMap); Person p = perMap.get("沈艳霞"); System.out.println(p); System.out.println(perMap.containsKey("吴定会")); System.out.println(perMap.containsValue(p)); System.out.println(perMap.keySet()); System.out.println(perMap.values()); } }
输出如下:
{吴定会=吴定会-50-true, 沈艳霞=沈艳霞-46-false}
沈艳霞-46-false
true
true
[吴定会, 沈艳霞]
[吴定会-50-true, 沈艳霞-46-false]
原文:https://www.cnblogs.com/zyly/p/10634785.html