zoukankan      html  css  js  c++  java
  • Java编程思想之十一 持有对象

    如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序。

    11.1 泛型和类型安全的容器

    使用ArrayList:创建一个实例,用add()插入对象,然后用get()访问对象,此时需要使用索引,像数组一样,但不是要[]。

    import java.util.ArrayList;
    public class ApplesAndOrangesWithoutGenerice
    {
        @SuppressWarnings("unchecked")//不受检查异常的警告
        public static void main(String[] args)
        {
            ArrayList apples = new ArrayList();
            for (int i = 0; i < 3; i++)
            {
                apples.add(new Apple());
            }
            apples.add(new Orange());
            for (Object obj : apples)
            {
                System.out.println(((Apple) obj).id());
            }
        }
    }
    class Apple
    {
        private static long counter;
        private final long id = counter++;
    
        public long id()
        {
            return id;
        }
    }
    class Orange
    {
    }
    

    ArrayList保存的是Object。
    要想定义保存特定类型的ArrayLIst可以使用泛型<>。

    import java.util.ArrayList;
    
    public class ApplesAndOrangesWithoutGenerice
    {
        @SuppressWarnings("unchecked")//不受检查异常的警告
        public static void main(String[] args)
        {
            ArrayList<Apple> apples = new ArrayList();
            for (int i = 0; i < 3; i++)
            {
                apples.add(new Apple());
            }
            //apples.add(new Orange());
            for (Object obj : apples)
            {
                System.out.println(((Apple) obj).id());
            }
        }
    }
    
    class Apple
    {
        private static long counter;
        private final long id = counter++;
    
        public long id()
        {
            return id;
        }
    }
    
    class Orange
    {
    
    }
    

    当指定了某个类型作为泛型参数时,并不仅限于只能将该确切类型的对象放置到容器中,向上转型也可以像作用于其他类型一样作用于泛型。

    import java.util.ArrayList;
    public class ApplesAndOrangesWithoutGenerice
    {
        @SuppressWarnings("unchecked")//不受检查异常的警告
        public static void main(String[] args)
        {
            ArrayList<Apple> apples = new ArrayList();
            for (int i = 0; i < 3; i++)
            {
                apples.add(new A());
                apples.add(new B());
                apples.add(new C());
            }
            //apples.add(new Orange());
            for (Object obj : apples)
            {
                System.out.println(((Apple) obj).id());
            }
        }
    }
    class Apple
    {
        private static long counter;
        private final long id = counter++;
        public long id()
        {
            return id;
        }
    }
    class A extends Apple
    {
    }
    class B extends Apple
    {
    }
    class C extends Apple
    {
    }
    class Orange
    {
    
    }
    

    11.2 基本概念

    Java容器类类库的用途是保存对象,并将其划分为两个不同的概念:

    • Collection。一个独立元素序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而set不能保存重复元素。
    • Map。一组成对的键值对对象,允许你使用键来查找值。映射表允许我们使用另一个对象来查找某个对象,它也被称为关联数组,因为它将对象于另外一个对象关联到了一起。或者被称为字典
    List<Type> types=new ArrayList<Type>()
    

    ArrayList被向上转型为List。
    Conllection接口概括了序列的概念——一种存放一组对象的方式。

    import java.util.ArrayList;
    import java.util.Collection;
    
    public class SimpleCollection
    {
        public static void main(String[] args)
        {
            Collection<Integer> c = new ArrayList<Integer>();
            for (int i = 0; i < 10; i++)
            {
                c.add(i);
            }
            for (int i : c)
            {
                System.out.println(i);
            }
        }
    }
    
    

    11.3 添加一组元素

    Arrays.asList()方法接受一个数组或一个用逗号分隔的元素列表,并将其转换为一个List对象。Collections.addAll()方法接受一个Collection对象,以及一个数组或一个用逗号分隔的列表,将元素添加到Collection中。

    import java.util.*;
    public class AddingGroups
    {
        public static void main(String[] args)
        {
            Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4));
            Integer[] moreInts = {6, 7, 8, 9};
            collection.addAll(Arrays.asList(moreInts));
            Collections.addAll(collection,12,13,14);  Collections.addAll(collection,moreInts);
            List<Integer> list=Arrays.asList(13,14,15);
        }
    }
    

    Collection.addAll()成员方法只能接受一个Collection对象作为参数,因此不如Arrays.asList()和Collections.addAll()灵活,着两个方法都可以是可变参数。
    可以直接使用Arrays.asList()的输出,将它当成List,但在这种情况下,地城是数组。

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    public class AsListInference
    {
        public static void main(String[] args)
        {
            List<Snow> snow1 = Arrays.asList(new P(), new S());
            List<Snow> snow2 = Arrays.asList(new P(), new S(), new L());
            List<Snow> snow3 = Arrays.asList(new H(), new L());
            List<Snow> snow4 = new ArrayList<Snow>();
            Collections.addAll(snow4, new P(), new L());
            List<Snow> snow5 = Arrays.<Snow>asList(new L());//显示类型参数说明
        }
    }
    
    class Snow{}
    
    class P extends Snow{}
    
    class L extends P{}
    
    class H extends P{}
    
    class S extends Snow{}
    

    11.4 容器的打印

    import java.util.*;
    
    public class PrintingContainers
    {
        static Collection fill(Collection<String> coll)
        {
            coll.add("rat");
            coll.add("cat");
            return coll;
        }
    
        static Map fill(Map<String, String> map)
        {
            map.put("rat", "r");
            map.put("cat", "c");
            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>()));
        }
    }
    
    

    Collection在每个槽中只能保存一个元素,此类容器还包括List。Map每个槽内保存两个元素,键和值相关联的值。
    ArrayList和LinkedList都是List类型,从输出可以看出,它们都是按照被插入的顺序保存元素的。
    两者的不同之处不仅在于执行某写类型的操作时的性能。而且LinkedList包括的操作也多于ArrayList。
    HashSet、TreeSet和LinkedHashSet都是Set类型。
    Map使得你可以用键来查找对象,就像一个简单的数据库。键所关联的对象称为值。
    HashSet一样,HashMap也提供了最快的查找技术,也没有按照任何明显的顺序来保存元素。TreeMap按照比较结果的升序保存键,而LinkedHashMap则按照插入顺序保存键,同时还保留了HashMap的查询速度。

    11.5 List

    List可以将元素维护在特定的序列中。List接口在Collection的基础上添加大量的方法,使得可以在List的中间插入和移除元素。
    有两种类型的List:

    • 基本的ArrayList,它长于随机访问元素,但使在List的中间插入和移除元素时较慢。
    • LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面比较慢,但是它的特性集较ArrayList更大。

    11.6 迭代器

    持有事物是容器最基本的工作,要使用容器,必须对容器的确切类型编程。
    如果只要使用容器,不知道或者不关心容器的类型,要如何不重写代码就可以应用于不同类型的容器?
    使用迭代器:迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不需要指定序列底层的结构。迭代器通常被称为轻量级对象:创建它的代价很小,经常可以看到对迭代器的奇怪限制。
    比如Java中的Iterator只能单向移动。

    
    

    接受对象容器并传递它,从而再每个对象上都执行操作。
    创建一个display方法,不必知道容器的确切类型:

    
    

    display方法不包含任何有关它遍历的序列的类型信息:能够将遍历序列的操作与序列底层的结构分离。所以,迭代器统一了对容器的访问方式。

    11.6.1 ListIterator

    ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类访问。但ListIterator可以双向移动。

    
    

    11.7 LinkedList

    LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作比ArrayList更高效,但再随机访问操作要差一些。

    
    

    11.8 Stack

    栈通常是指后进先出的容器。有时也被称为叠加栈,因为最后压入栈的元素,第一个弹出栈。
    直接将LinkedList作为栈使用。

    import java.util.LinkedList;
    
    public class Stack<T>
    {
        LinkedList<T> storage=new LinkedList<T>();
        public void push(T t){storage.addFirst(t);}//插入一个元素
        public T peek(){return storage.getFirst();}//获取第一个元素
        public T pop(){return storage.removeFirst();}//移除第一个元素
        public boolean empty(){return storage.isEmpty();}//是否还有元素
        public String toString(){return storage.toString();}
    }
    

    创建一个新的Stack类:

    public class StackTest
    {
        public static void main(String[] args){
            Stack<String> stack=new Stack<String>();
            for(String s:"My dog has fleas".split(" "))
                stack.push(s);
            while (!stack.empty())
                System.out.println(stack.pop());
        }
    }
    

    11.9 Set

    Set不保存重复的元素。
    Set具有与Collection完全一样的接口。实际上Set就是Collection,只是行为不同。Set基于对象的的值来确定归属性。

    import java.util.HashSet;
    import java.util.Random;
    import java.util.Set;
    import java.util.TreeSet;
    
    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<10;i++)
                intset.add(rand.nextInt(30));
            for(int i:intset)
                System.out.println(i);
        }
    }
    
    

    HashSet所维护的顺序与TreeSet或LinkedHashSet都不同。TreeSetj将元素存储再红——黑树数据结构中,而HashSet使用的是散列函数。LinkedHashList因为查询速度的原因也使用了散列。
    对结构排序,使用TreeSet来代替HashSet

    import java.util.HashSet;
    import java.util.Random;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class SetOfInteger
    {
        public static void main(String[] args){
            Random rand=new Random(47);
            Set<Integer> intset=new TreeSet<Integer>();
            for (int i=0;i<10;i++)
                intset.add(rand.nextInt(30));
            for(int i:intset)
                System.out.println(i);
        }
    }
    
    

    使用contains()测试Set归属性

    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    public class SetOperations
    {
        public static void main(String[] args){
            Set<String> set1=new HashSet<String>();
            Collections.addAll(set1,"a b c d e f g".split(" "));
            set1.add("m");
            System.out.println("m:"+set1.contains("m"));
            System.out.println("h:"+set1.contains("h"));
        }
    }
    
    

    打开文件,并将其读入一个Set中。
    TextFile继承自List< Sting >,其构造器将打开文件,并更具正则表达式将其断开。
    如果要按照字母排序,可以向TreeSet构造器传入String.CASE_INSENTIVE_ORDER比较器。

    import net.mindview.util.TextFile;
    
    import java.util.Set;
    import java.util.TreeSet;
    
    public class UniqueWordsAlphabetic
    {
        public static void main(String[] args){
            Set<String> words=new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
            words.addAll(new TextFile("第十一章持有对象\src\SimpleCollection.java","\W+"));
            System.out.println(words);
        }
    }
    

    11.10 Map

    将对象映射到其他对象的能力是一种解决编程问题的杀手锏。

    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class MapOfList
    {
        public static Map<Integer, List<? extends Person>> petPeople = new HashMap<Integer, List<? extends Person>>();
        static {
            petPeople.put(1, Arrays.asList(new Man("aaa"),new WoMan("bbb")));
        }
        public static void main(String[] args){
            System.out.println(petPeople.keySet());
            System.out.println(petPeople.values());
            System.out.println(petPeople);
        }
    }
    class Person
    {
        private String name;
        Person(String name){
            this.name=name;
        }
    }
    class Man extends Person
    {
        public Man(String name){
            super(name);
        }
    }
    class WoMan extends Person
    {
        WoMan(String name){
            super(name);
        }
    }
    

    Map可以返回它的键的Set,它的值Collection,或者它的键值对的Set。

    11.11 Queue

    队列是一个典型的先进先出的容器。队列在并发编程中特别重要,因为它们可以安全地将对象从一个任务传输给另一个任务。
    LinkedLisk提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedLisk可以用作Queue的一种实现。

    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Random;
    
    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 ran=new Random();
            for(int i=0;i<10;i++)
                queue.offer(ran.nextInt(i+10));
            printQ(queue);
            Queue<Character> qc=new LinkedList<Character>();
            for (char c:"Brontosaurus".toCharArray())
                qc.offer(c);
            printQ(qc);
        }
    }
    

    offer()方法将一个元素插入到队尾,或者返回false。peek()和element()都将在不移除的情况下返回对头。poll()和remove()方法将移除并返回对头。

    11.11.1 PriorityQueue

    优先级队列声明下一个弹出元素最需要的元素。
    当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认排序将使用对象在队列中自然顺序,但你可以通过提供自己的Comparator来修改这个顺序。

    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);
            priorityQueue = new PriorityQueue<Integer>(ints);
            QueueDemo.printQ(priorityQueue);
    
            priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.reverseOrder());
            priorityQueue.addAll(ints);
            QueueDemo.printQ(priorityQueue);
    
            String face = "EDUCATION SHOULD ESCHEW";
            List<String> strings = Arrays.asList(face.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> characters = new HashSet<Character>();
            for (char c : face.toCharArray())
                characters.add(c);
            PriorityQueue<Character> characterspc = new PriorityQueue<Character>(characters);
            QueueDemo.printQ(characterspc);
        }
    
    }
    

    在PriorityQueue重复是允许的,最小值拥有最高优先级。

    11.12 Collection和Iterator

    Collection是描述所以有序列容器的共性的根接口,它可能会被认为是一个附属接口,即因为要表示其他若干接口的共性出现的接口。
    通过针对接口而非具体实现编写代码,我们的代码可以应用于更多的对象类型。

    import java.util.*;
    
    public class InterfaceVsIterator
    {
        public static void display(Iterator<Integer> it)
        {
            while (it.hasNext())
            {
                Integer i = it.next();
                System.out.print(i+",");
            }
            System.out.println();
        }
        public static void display(Collection<Integer> ints)
        {
            for(Integer i:ints)
                System.out.print(i+"-");
            System.out.println();
        }
        public static void main(String[] args)
        {
            List<Integer> intList= Arrays.asList(1,2,3,15,3);
            Set<Integer> intSet=new HashSet<Integer>(intList);
            Map<String,Integer> intmap=new LinkedHashMap<String,Integer>();
            String[] s=new String[]{"A","b","c","f","w"};
            for(int i=0;i<s.length;i++)
                intmap.put(s[i],intList.get(i));
            display(intList);
            display(intSet);
            display(intList.iterator());
            System.out.println(intmap);
            System.out.println(intmap.keySet());
            display(intmap.values());
            display(intmap.values().iterator());
        }
    }
    

    Collection和Iterator都可以将display()方法与底层容器的特定实现解耦。

    import java.util.AbstractCollection;
    import java.util.Iterator;
    
    public class CollectionSequence extends AbstractCollection<Integer>
    {
        private Integer[] ints=new Integer[]{1,4,2,5,1};
        @Override
        public Iterator<Integer> iterator()
        {
            return new Iterator<Integer>()
            {
                private int index=0;
                @Override
                public boolean hasNext()
                {
                    return index<ints.length;
                }
    
                @Override
                public Integer next()
                {
                    return ints[index++];
                }
            };
        }
        @Override
        public int size()
        {
            return ints.length;
        }
    
        public static void main(String[] args){
            CollectionSequence c=new CollectionSequence();
            InterfaceVsIterator.display(c);
            InterfaceVsIterator.display(c.iterator());
        }
    }
    

    生成Iterator是将队列与消费队列的方法连接在一起耦合度最小的方式,并且与1实现Collection相比,它在序列类上所加的约束少得多。

    import java.util.Iterator;
    
    public class NonCollectionSequence extends PetSequence
    {
        public Iterator<Integer> iterator()
        {
            return new Iterator<Integer>()
            {
                int index = 0;
    
                @Override
                public boolean hasNext()
                {
                    return index < ints.length;
                }
    
                @Override
                public Integer next()
                {
                    return ints[index++];
                }
            };
        }
        public static void main(String[] args){
            NonCollectionSequence nc=new NonCollectionSequence();
            InterfaceVsIterator.display(nc.iterator());
        }
    }
    
    class PetSequence
    {
        protected int[] ints = new int[]{1, 4, 2, 5, 3};
    }
    

    11.13 Foreach与迭代器

    foreach语法主要用于数组,但它也可以用于任何Collection对象。
    Java SE5引入了新的被称为Iterable接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此,如果你创建了一个Iterable的类,都可以用于foreach。

    import java.util.Iterator;
    
    public class IterableClass implements Iterable<String>
    {
        protected String[] words = ("a,d,fe,r,gd,v,g").split(",");
    
        @Override
        public Iterator<String> iterator()
        {
            return new Iterator<String>()
            {
                int index = 0;
    
                @Override
                public boolean hasNext()
                {
                    return index < words.length;
                }
    
                @Override
                public String next()
                {
                    return words[index++];
                }
            };
        }
        public static void main(String[] args){
            for(String s:new IterableClass())
                System.out.print(s+",");
        }
    }
    

    在Java SE中,大量的类都是Iterable类型,主要包括所有Collection类(不包括各种Map)
    foreach可以用于数组或其他任何Iterable,但是这并不意味着数组肯定也是一个Iterable,二任何自动包装也不会发生。

    import java.util.Arrays;
    
    public class ArrayIsMotIterable
    {
        static<T> void test(Iterable<T> ib){
            for(T t:ib)
                System.out.print(t+" ");
        }
        public static void main(String[] args){
            test(Arrays.asList(1,2,3,2));
            String[] strings={"a","b","c"};
            //test(strings);//转换失败
            test(Arrays.asList(strings));
        }
    }
    

    尝试把数组转换成Iterable会失败,不存在任何数组到Iterable的自动转换,必须手工执行这种转换。

    11.13.1 适配器方法惯用法

    希望在默认向前的迭代器基础上,添加产生反向迭代器的能力,因此,不能使用覆盖,需要添加一个能够产生Iterable对象的方法,该对象可以用于foreach语句。

    mport java.util.Iterator;
    
    public class IterableClass implements Iterable<String>
    {
        protected String[] words = ("a,d,fe,r,gd,v,g").split(",");
    
        @Override
        public Iterator<String> iterator()
        {
            return new Iterator<String>()
            {
                int index =words.length-1 ;
    
                @Override
                public boolean hasNext()
                {
                    return index > -1;
                }
    
                @Override
                public String next()
                {
                    return words[index--];
                }
            };
        }
        public static void main(String[] args){
            for(String s:new IterableClass())
                System.out.print(s+",");
        }
    
    }
    

    如果直接将ral对象置于foreach语句中,将得到向前迭代器。但如果在对象上调用产生向后迭代器的方法,就会产生不同的行为了。
    通过这种方式,可以添加两种适配器方法。

  • 相关阅读:
    8皇后问题
    初级8皇后问题
    某个集合的子集问题
    n全排列输出和 n个数的组合(数字范围a~b)
    (转)李明博:我的12年等于24年 快速提升的秘诀是什么 别人以为我早起是先天的,事实靠的是努力 训练,除了反复的努力之外没有什么别的秘诀 像企业主一样去思考,一样查找问题,一同去解决它,并且还要制定出比企业主要求更高的目标。李明博像我一样,不,他比我更把公司当成自己的
    (转)当别人努力的时候,你在做什么? 评论事情的一种态度 当你在抱怨的时候,为什么不想想我做了什么? 把简单的原则坚持贯彻下去 消极的心态,养成了惯性的思维,一切都是不好的。 也许这就是人性的弱点,不经意的习惯,却逐渐腐蚀了你的人生。
    对于保险的看法和如何拒绝保险推销 保险应该主要是有2个主要作用: 1. 分担风险 2. 投资 保险的常用推销方法和该保险的卖点 拒绝保险的方法
    业务、架构、技术,我们应该关注什么 Java和.Net的优势劣势简单看法 市场经济决定,商业之道即是软件之道,市场的需求决定着软件技术的发展 利益决定着选择应用新技术
    我的学习工作经历,一个园林专业中专毕业生的IT之路 学习编程 创业
    “医疗信息化行业之中的联发科”- 我们在医疗行业中的定位及目标 想做一个面对中小企业的专业上游软件供应商 台湾联发科技颠覆掉的是一个封闭的手机产业系统 解决方案,即AgileHIS.NET数字化医院基础方案
  • 原文地址:https://www.cnblogs.com/Tan-sir/p/11235133.html
Copyright © 2011-2022 走看看