• Java学习笔记(6)——对象容器


    1.ArrayList

    ArrayList类别实作了List接口,List接口是Collection接口的子接口,主要增加了根据索引取得对象的方法。

    ArrayList使用数组实作List接口,所以对于快速的随机取得对象来说,使用ArrayList可以得到较好的效能,不过在移除对象或插入对象时,ArrayList就比较慢(使用 LinkedList 在这方面就好的多)。

    来看看一个ArrayList的范例:

    ArrayListDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class ArrayListDemo {
    	public static void main(String[] args) {
    		Scanner scanner = new Scanner(System.in);
    		List list = new ArrayList();
    		System.out.println("输入名称(quit结束)");
    		while(true) {
    			System.out.print("# ");
    			String input = scanner.next();
    			if(input.equals("quit"))
    			break;
    			list.add(input);
    		}
    		System.out.print("显示输入: ");
    		for(int i = 0; i < list.size(); i++)
    		System.out.print(list.get(i) + " ");
    		System.out.println();
    	}
    }

    在 J2SE 5.0 之后新增了泛型(Generic)的功能,使用对象容器时建议容器中将储存的对象型态,如此您的对象在存入容器会被限定为您所宣告的型态,而取出时,也不至于失去原来的型态信息,可以避免型态转换时的问题。

    使用add()方法可以将一个对象加入ArrayList中,使用size()方法可以传回目前的ArrayList的长度,使用get()可以传回指定索引处的对象,使用toArray()可以将ArrayList中的对象转换为对象数组。

    以下是执行结果:

    输入名称(quit结束)
    # Justin
    # caterpillar
    # momor
    # quit
    显示输入: Justin caterpillar momor?

    您可以使用get()方法指定索引值取出对象,然而如果您的目的是要循序取出容器中所有的对象,则您可以使用Iterator类,Iterator类实作 Iterator 模式,实际来看个例子:

    ArrayListDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class ArrayListDemo {
    	public static void main(String[] args) {
    		Scanner scanner = new Scanner(System.in);
    		List list = new ArrayList();
    		System.out.println("输入名称(quit结束)");
    		while(true) {
    			System.out.print("# ");
    			String input = scanner.next();
    			if(input.equals("quit"))
    			break;
    			list.add(input);
    		}
    		Iterator iterator = list.iterator();
    		while(iterator.hasNext()) {
    			System.out.print(iterator.next() + " ");
    		}
    		System.out.println();
    	}
    }
    

    iterator()方法会传回一个Iterator对象,这个对象提供的遍访的方法,hasNext()方法测试Iterator中是否还有对象,如果 有的话,可以使用next()取出。

    事实上,在J2SE 5.0您也不必须使用iterator()了,使用增强的for循环可以直接遍访List的所有元素,例如:

    ArrayListDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class ArrayListDemo {
    	public static void main(String[] args) {
    		Scanner scanner = new Scanner(System.in);
    		List list = new ArrayList();
    		System.out.println("输入名称(quit结束)");
    		while(true) {
    			System.out.print("# ");
    			String input = scanner.next();
    			if(input.equals("quit"))
    			break;
    			list.add(input);
    		}
    		for(String s : list) {
    			System.out.print(s + " ");
    		}
    		System.out.println();
    	}
    }

    2.LinkedList

    List类是以对象加入(add)容器的顺序来排列它们,如果您的对象加入之后大都是为了取出,而不会常作移除或插入(Insert)的动作,则使用ArrayList,如果您会经常从容器中作移除或插入对象的动作,则使用LinkedList会获得较好的效能。

    LinkedList实作了List接口,并增加了一些移除与插入对象的特定方法,像是addFirst()、addLast()、 getFirst()、getLast()、removeFirst( )、removeLast()等等,由于在插入与移除时有较好的效能,适合拿来实作堆栈(Stack)与队列(Queue)。

    以下实作一个简单的FILO(First-In, Last-Out)堆栈,可以存入字符串:

    StringStack.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class StringStack {
    	private LinkedList linkedList;
    	public StringStack() {
    		linkedList = new LinkedList();
    	}
    	public void push(String name) {
    		linkedList.addFirst(name);
    	}
    	public String top() {
    		return linkedList.getFirst();
    	}
    	public String pop() {
    		return linkedList.removeFirst();
    	}
    	public boolean isEmpty() {
    		return linkedList.isEmpty();
    	}
    }

    而对于FIFO(First-In, First-Out)的队列,我们也可以使用LinkedList来实作:

    StringQueue.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class StringQueue {
    	private LinkedList linkedList;
    	public StringQueue() {
    		linkedList = new LinkedList();
    	}
    	public void put(String name) {
    		linkedList.addFirst(name);
    	}
    	public String get() {
    		return linkedList.removeLast();
    	}
    	public boolean isEmpty() {
    		return linkedList.isEmpty();
    	}
    }

    事实上,如果您要使用队列的功能,您也不用亲自实作,在J2SE 5.0中,LinkedList也实作了新加入的java.util.Queue接口,这个接口有五个必须实作的方法:

    element()

    取得但不移除队列第一个组件,队列为空时会丢出例外

    offer()

    加入一个元素至队列中

    peek()

    取得但不移除队列第一个组件

    poll()

    取得并移去队列第一个组件,队列为空时传回null

    remove()

    取得并移除队列第一个组件

    要使用队列的功能,您只要类似这样的宣告:

    Queue<String> queue = new LinkedList<String>();

    3.HashSet

    HashSet实作Set接口,Set接口继承Collection接口,Set容器中的对象都是唯一的,加入 Set容器中的对象都必须重新定义equals()方法,作为唯一性的识别,Set容器有自己的一套排序规则。

    HashSet的排序规则是利用Hash Table,所以加入HashSet容器的对象还必须重新定义hashCode()方法,利用Hash的方式,可以让您快速的找到容器中的对象,在比较两个加入Set容器中的对象是否相同时,会先比较hashCode()方法传回的值是否相同,如果相同,则再使用equals()方法比较,如果两者都相同,则视为相同的对象。

    事实上,在撰写新的类别时,最好总是重新定义equals()与hashCode()方法,以符合Java的设计规范,您可以参考 Object 类别 中的介绍了解如何重新定义equals()与hashCode()。
    来看一个例子:

    HashSetDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class HashSetDemo {
    	public static void main(String[] args) {
    		Set set = new HashSet();
    		set.add("caterpillar");
    		set.add("justin");
    		set.add("momor");
    		set.add("justin");
    		Iterator iterator = set.iterator();
    		while(iterator.hasNext()) {
    			System.out.print(iterator.next() + " ");
    		}
    		System.out.println();
    	}
    }

    执行结果:

    momor  justin caterpillar

    如上所示的,即使重复加入了"justin"字符串,HashSet中仍只有一个"justin"字符串对象,另一个要注意的是,选代所有的值时,其顺序 与您加入的顺序是不一样的,选代所有值时的顺序是HashSet排序过后的顺序。

    LinkedHashSet是HashSet的子类,它在内部实作使用Hash Code进行排序,然而允许您在列举时行为像是LinkedList,简单的改写上面的程序即可了解:

    LinkedHashSetDemo.java
    package onlyfun.caterpillar;
    
    import java.util.*;
    
    public class LinkedHashSetDemo {
    public static void main(String[] args) {
    Set set = new LinkedHashSet();
    
    set.add("caterpillar");
    set.add("justin");
    set.add("momor");
    set.add("justin");
    
    Iterator iterator = set.iterator();
    while(iterator.hasNext()) {
    System.out.print(iterator.next() + " ");
    }
    
    System.out.println();
    }
    }

    执行结果:

    caterpillar  justin momor

    可以在执行结果中看到的,选代时的顺序正是您加入值的顺序。

    4.TreeSet

    TreeSet实作Set接口与SortedSet接口,提供相关的方法让您有序的取出对应位置的对象,像是 first()、last()等方法,TreeSet是J2SE中唯一实作SortedSet接口的类别,它使用红黑树结构来对加入的对象进行排序。

    看个简单的例子:

    TreeSetDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class TreeSetDemo {
    	public static void main(String[] args) {
    		Set set = new TreeSet();
    		set.add("justin");
    		set.add("caterpillar");
    		set.add("momor");
    		set.add("justin");
    		Iterator iterator = set.iterator();
    		while(iterator.hasNext()) {
    			System.out.print(iterator.next() + " ");
    		}
    		System.out.println();
    	}
    }

    由于加入的是String对象,执行结果会自动依字典顺序进行排序的动作:

    caterpillar  justin momor

    依字典顺序排序String对象是TreeSet预设的,如果您对对象有自己的一套排序顺序,您要实作一个 Comparator 对象,您要实作compare()方法,它必须传回整数值,如果对象顺序相同则传回0,传回正整数表示compare()方法的第一个对象大于第二个对象,反之则传回负整数。

    举个实际的例子,假设您想要改变TreeSet依字典顺序排列加入的对象为相反的顺序:

    CustomComparator.java
    package onlyfun.caterpillar;
    import java.util.Comparator;
    public class CustomComparator implements Comparator {
    	public int compare(T o1, T o2) {
    		if (((T) o1).equals(o2))
    		return 0;
    		return ((Comparable) o1).compareTo((T) o2) * -1;
    	}
    }

    在自订的Comparator中,如果两个对象的顺序相同会传回0,这在TreeSet中表示两个对象是同一个对象,TreeSet要求传入的对象必须实 作java.lang.Comparable接口,范例中只是简单的将原来compareTo()传回的值乘以负一,如此在TreeSet中就可以简单的 让排列顺序相反。

    在建构TreeSet实例时一并指定自订的Comparator,例如:

    TreeSetDemo2.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class TreeSetDemo2 {
    	public static void main(String[] args) {
    		// 自订Comparator
    		Comparator comparator =
    		new CustomComparator();
    		Set set =
    		new TreeSet(comparator);
    		set.add("justin");
    		set.add("caterpillar");
    		set.add("momor");
    		// 使用 enhanced for loop 显示对象
    		for(String name : set) {
    			System.out.print(name + " ");
    		}
    		System.out.println();
    	}
    }
    

    执行的结果是相反的:

    momor justin  caterpillar
    
    

    5.EnumSet

    EnumSet的名称说明了其作用,它是在J2SE 5.0后加入的新类别,可以协助您建立列举值的集合,它提供了一系列的静态方法,可以让您指定不同的集合建立方式,例如:

    EnumSetDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    enum FontConstant { Plain, Bold, Italic }
    public class EnumSetDemo {
    	public static void main(String[] args) {
    		EnumSet enumSet =
    		EnumSet.of(FontConstant.Plain, FontConstant.Bold);
    		showEnumSet(enumSet);
    		showEnumSet(EnumSet.complementOf(enumSet));
    	}
    	public static void showEnumSet(
    	EnumSet enumSet) {
    		Iterator iterator = enumSet.iterator();
    		while(iterator.hasNext()) {
    			System.out.print(iterator.next() + " ");
    		}
    		System.out.println();
    	}
    }
    

    您可以指定列举值来加入EnumSet中,of()方法会返回一个EnumSet的实例,当中包括您所指定的列举值,您也可以使complementOf()指定一个EnumSet的互补集,以下是执行的结果:

    Plain Bold 
    Italic?

    EnumSet实作了Set接口,所以您可以使用Set接口的所有方法来测试它所包括的列举值,例如测试一个集合中是否包括 FontConstant.Bold:

    if(enumSet.contains(FontConstant.Bold)) {
    	....
    }

    您也可以建立一个空的EnumSet,然后自己逐个加入列举值,例如:

    EnumSetDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    enum FontConstant { Plain, Bold, Italic }
    public class EnumSetDemo {
    	public static void main(String[] args) {
    		EnumSet enumSet =
    		EnumSet.noneOf(FontConstant.class);
    		enumSet.add(FontConstant.Bold);
    		enumSet.add(FontConstant.Italic);
    		showEnumSet(enumSet);
    	}
    	public static void showEnumSet(
    	EnumSet enumSet) {
    		Iterator iterator = enumSet.iterator();
    		while(iterator.hasNext()) {
    			System.out.print(iterator.next() + " ");
    		}
    		System.out.println();
    	}
    }
    

    执行结果:

    Bold Italic

    您也可以由一个容器对象中建立EnumSet:

    List list = new ArrayList();
    list.add(FontConstant.Bold);
    list.add(FontConstant.Italic);
    
    showEnumSet(EnumSet.copyOf(list));

    更多EnumSet相关的方法,您可以参考 EnumSet 在线API文件。

    6.HashMap

    HashMap实作Map接口,内部实作使用Hash Table,让您在常数时间内可以寻得key/value对。

    所谓的key/value对,简单的说,您将Map容器对象当作一个有很多间房间的房子,每个房间的门有一把钥匙,您将对象储存至房间中时,要顺便拥有一把钥匙,下次要取回对象时,就是根据这把钥匙取得。

    以一个简单的例子来作说明:

    HashMapDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class HashMapDemo {
    	public static void main(String[] args) {
    		Map< String, String> map =
    		new HashMap< String, String>();
    		map.put("caterpillar", "caterpillar's message!!");
    		map.put("justin", "justin's message!!");
    		System.out.println(map.get("justin"));
    		System.out.println(map.get("caterpillar"));
    	}
    }
    
    

    在宣告Map型态时,您指定了key/value各自的型态,这边都是宣告为String,也就是以String对象作为key对象的型态,而 value也是以String对象作为其型态。

    使用Map的put()方法将对象存入,必须同时指定key/value,而要取回对象时,则指定key,程序的执行结果如下:

    justin's message!!
    caterpillar's message!!

    HashMap是个被经常使用的对象,您可以参考下面几个例子中HashMap的应用:

    • Command 模式
    • Thread-Specific Storage 模式
    • 控 制器(Servlet)

    可以使用values()方法返回一个Collection对象,如果您需要一次选代Map中所有的对象,这会很有用,例如:

    HashMapDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class HashMapDemo {
    	public static void main(String[] args) {
    		Map< String, String> map =
    		new HashMap< String, String>();
    		map.put("justin", "justin's message!!");
    		map.put("momor", "momor's message!!");
    		map.put("caterpillar", "caterpillar's message!!");
    		Collection collection = map.values();
    		Iterator iterator = collection.iterator();
    		while(iterator.hasNext()) {
    			System.out.println(iterator.next());
    		}
    	}
    }
    
    

    执行结果:

    momor's message!!
    justin's message!!
    caterpillar's message!!

    HashMap使用Hash Table,因而它有自己的排序方式,如果您想要在选代所有的对象时,依照插入的顺序来排序,则可以使用LinkedHashMap,它是HashMap 的子类,使用values()所返回的Collection对象,其内含对象之顺序即为当初您加入对象之顺序,例如:

    LinkedHashMapDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class LinkedHashMapDemo {
    	public static void main(String[] args) {
    		Map< String, String> map =
    		new LinkedHashMap< String, String>();
    		map.put("justin", "justin's message!!");
    		map.put("momor", "momor's message!!");
    		map.put("caterpillar", "caterpillar's message!!");
    		Collection collection = map.values();
    		Iterator iterator = collection.iterator();
    		while(iterator.hasNext()) {
    			System.out.println(iterator.next());
    		}
    	}
    }
    

    执行结果:

    justin's message!!
    momor's message!!
    caterpillar's message!!
    
    

    7.TreeMap

    TreeMap实作Map接口与SortedMap接口,提供相关的方法让您有序的取出对应位置的对象,像是 firstKey()、lastKey()等方法,TreeMap是J2SE中唯一实作SortedMap接口的类别,它使用红黑树结构来对加入的对象进 行排序

    看个简单的例子:

    TreeMapDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    public class TreeMapDemo {
    	public static void main(String[] args) {
    		Map< String, String> map =
    		new TreeMap< String, String>();
    		map.put("justin", "justin's message!!");
    		map.put("momor", "momor's message!!");
    		map.put("caterpillar", "caterpillar's message!!");
    		Collection collection = map.values();
    		Iterator iterator = collection.iterator();
    		while(iterator.hasNext()) {
    			System.out.println(iterator.next());
    		}
    	}
    }
    

    由于您加入的是String对象,执行结果会自动依key的字典顺序进行排序的动作:

    caterpillar's message!!
    justin's message!!
    momor's message!!

    依字典顺序排序String对象是TreeMap预设的,如果您对对象有自己的一套排序顺序,您要实作一个 Comparator 对象,它有两个必须实作的方法,compare()与equals(),前者必须传回整数值,如果对象顺序相同则传回0,传回正整数表示compare ()方法的第一个对象大于第二个对象,反之则传回负整数,后者则是定义两个对象是否相等。

    8、EnumMap

    EnumMap是个专为列举型别设计的类别,方便您使用列举型别及Map对象,直接来举个实例:

    EnumMapDemo.java
    package onlyfun.caterpillar;
    import java.util.*;
    enum Action {TURN_LEFT, TURN_RIGHT, SHOOT}
    public class EnumMapDemo {
    	public static void main(String[] args) {
    		Map< Action, String> map =
    		new EnumMap< Action, String>(Action.class);
    		map.put(Action.TURN_LEFT, "向左转");
    		map.put(Action.TURN_RIGHT, "向右转");
    		map.put(Action.SHOOT, "射击");
    		for(Action action : Action.values( ) ) {
    			System.out.println(map.get(action));
    		}
    	}
    }
    

    执行结果:

    向左转
    向右转
    射击

    与单纯的使用HashMap比较起来的差别是,在上面的程序中,EnumMap将根据列举的顺序来维护对象的排列顺序,从下面这个程序可以看个大概:

    EnumMapDemo2.java
    package onlyfun.caterpillar;
    import java.util.*;
    enum Action {TURN_LEFT, TURN_RIGHT, SHOOT}
    public class EnumMapDemo2 {
    	public static void main(String[] args) {
    		Map< Action, String> map =
    		new EnumMap< Action, String>(Action.class);
    		map.put(Action.SHOOT, "射击");?
    		map.put(Action.TURN_RIGHT, "向右转");
    		map.put(Action.TURN_LEFT, "向左转");
    		for(String value : map.values( )) {
    			System.out.println(value);
    		}
    	}
    }
    

    执行结果:

    向左转
    向右转
    射击

    从遍访的结果可以看出,对象的顺序是根据列举顺序来排列的

    字节跳动内推

    找我内推: 字节跳动各种岗位
    作者: ZH奶酪(张贺)
    邮箱: cheesezh@qq.com
    出处: http://www.cnblogs.com/CheeseZH/
    * 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    ZQUOJ 1964 Hamilton回路(状压dp)
    ZQUOJ 1398 Hamilton路径(DFS,稀疏图,n≤40)
    2019 CCPC
    百度之星初赛第三场 1002 最短路2 (Floyd算法优化)
    2019牛客暑期多校训练营(第四场)J free(分层图最短路/模板题)
    2019牛客暑期多校训练营(第一场)I Points Division(DP+线段树)
    2019牛客暑期多校训练营(第七场)A String(枚举)
    2019牛客暑期多校训练营(第九场)B Quadratic equation(欧拉准则+解二次剩余)
    二次剩余系解法
    二次剩余的判断(欧拉准则)
  • 原文地址:https://www.cnblogs.com/CheeseZH/p/2803908.html
Copyright © 2020-2023  润新知