Java基础 -- 持有对象(容器)
目录
一 容器的用途
如果对象的数量与生命周期都是固定的,自然我们也就不需要很复杂的数据结构。
我们可以通过创建引用来持有对象,如
Class clazz;
也可以通过数组来持有多个对象,如
Class[] clazs = new Class[10];
然而,一般情况下,我们并不知道要创建多少对象,或者以何种方式创建对象。数组显然只能创建固定长度的对象,为了使程序变得更加灵活与高效,Java类库提供了一套完整的容器类,具备完善的方法来解决上述问题。
从上图可以看到容器中有七大接口:
- Collection接口
- Map接口
- Set接口
- List接口
- Queue接口
- Iterator接口
- Comparable接口
其中List, Queue和Set接口继承了Collection接口,剩下的接口之间都是相互独立的,无继承关系。Iterater迭代器则是为了更灵活的迭代集合,与foreach一起使用。Comparable接口则用于比较。
除了接口之外,容器中还包含了List接口的实现类LinkedList,Set接口的实现类HashSet等;容器中还包含两个工具类/帮助类:Collections和Arrays,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
容器主要分为两种类型:
- Collection:用来保存单一的元素序列,这些元素都服从一条或者多条规则。List必须按照插入的顺序保存元素,而Set不能有重复的元素,Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
- Map:用来保存关联键值对,允许使用键来查找值。ArrayList允许你使用数字来查找值,因此在某种意义上讲,它将数字与对象关联在了一起。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”,因为它将某些对象与另外一些对象关联在一起;或者称为"字典",因此可以使用键对象来查找值对象,就像在字典中使用单词来定义一样。
二 Collection中添加元素
在java.util包中的Collections和Arrays类中包含了很多使用方法,可以在一个Collection中添加一组元素。
- Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个Lsit对象。
- Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分隔的列表,将元素添加到Collection中。
如下演示一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | ///添加一组元素 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 | [ 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类型可能会引发问题。
Collections了类中还提供了另一个函数fill(),其函数原型为:public static <T> void fill(List<? super T>,Tobj):使用同一个元素填充list集合中的每一元素,示例如下:
//方式三 List<Integer> ints = new ArrayList<Integer>(); Collections.addAll(ints, 1,2,3,4,5); Collections.fill(ints, 45); //fill(List<? super T,T obj>) System.out.println(ints);
输出如下:
[45, 45, 45, 45, 45]
三 容器的打印
容器的输出,默认调用的是容器.toString()方法,如下:
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 30 31 32 | ///打印容器 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>())); } } |
输出如下:
1 2 3 4 5 6 7 8 | [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} |
四 List接口
相比于Collection接口,添加了新的方法,使得可以在List中间插入和移除元素。其实现类有以下两个:
- ArrayList:类似于动态数组,适用于大量随机访问的情况,但是插入和删除的代价很高;
- LinkedList:LinkedList除了实现了List接口,类似于链表,提供了优化的顺序访问,在插入和删除访问方面代价低廉,但是随即访问代价较高;由于LinkedList还实现了Queue接口,可以使其用作栈、队列或双端队列。
1、ArrayList
下面演示一个ArrayList的例子,将ArrayList向上转型为List:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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 2 3 4 5 6 7 8 9 10 | 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的部分方法:
- contains():判断一个对象是否在列表中。
- remove():删除一个对象,如果你想移除一个对象,则可以将这个对象的引用传递给这个方法;
- indexOf();用来判断一个对象在List中所处位置的索引编号,不存在返回-1;
- subList():从列表中创建一个子集;
- containsAll():判断是否存在该子集;
- removeAll():移除一个子集;
- isEmpty():判断是否为空;
除了以上方法,List还有很多方法,比如set()、replace()、clear()、addAll()、toArray()等。
2、LinkedList
下面演示一个LinkedList的例子
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 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()); } } |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 | [吴定会- 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中,正因为如此,查找就成为了Set中最重要的操作。
具有与Collection相同的方法。实际上Set就是Collection,只是行为不同。其实现类有以下两个:
- HashSet:使用了散列函数实现元素的存储,极大的提高了访问速度,存入HashSet的对象必须定义hashCode()。注意:HashSet元素存储的顺序看起来没有实际意义。
- TreeSet:使用红黑树来实现存储,好处就是可以在插入之后维持集合的有序性,按照比较结果升序保存元素。
此外还有一个类型LinkedHashSet,继承自HashSet:
- LinkedHashSet:使用链表保存元素插入顺序,但是它也使用了散列,保留了HashSet的访问速度。
1、HashSet
下面是使用存放Integer对象的HashSet的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } } |
输出结果:
1 | [ 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。
2、TreeSet
如果想对插入的元素进行排序,可以使用TreeSet。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } } |
输出如下:
1 | [ 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 ] |
六 Queue接口
队列是一个典型的先进先出(FIFO)的容器,即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要。
Queue的实现有以下两个:
- LinkedList:LinkedList实现了Queue接口,提供了支持队列行为的方法;
- PriorityQueue:与普通队列不同,优先队列每次弹出的是优先级最高的元素,可以通过提供自己的Comparator来修改默认的优先级顺序。
1、LinkedList
下面的示例使用了Queue接口中与Queue相关的方法:
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 | 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); } } |
输出如下:
1 2 | 8 1 1 1 5 14 3 1 0 1 L a J i S h i P i n |
下面介绍一下上面用到的方法:
- offer():它在允许的情况下,将一个元素插入到队尾,或者返回false;
- peek():在不移除的情况下返回队头,在队列为空时返回null;
- element():在不移除的情况下返回队头,在队列为空时抛出NoSuchElementException异常;
- poll():移除并返回队头,在队列为空时返回null;
- remove():移除并返回队头,在队列为空时抛出NoSuchElementException异常;
自动装包机制会自动将nextInt()方法的int结果转换为Integer对象,将char转换成所需的Character对象。Queue接口窄化了对LinkedList的方法的访问权限,以使得只有恰当的方法可以使用,因此能够访问的LinkedList方法会变少(这里我们可以将queue转换回LinkedList,但是最好不要这么做)。
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2018-04-02 第三节,TensorFlow 使用CNN实现手写数字识别(卷积函数tf.nn.convd介绍)
2018-04-02 数据处中模块之matplotlib