Java 基础之类集
来源:互联网 发布:网络购物市场交易规模 编辑:程序博客网 时间:2024/06/07 23:11
平时我们用 List 、 Map 用得挺 Happy 的,只知道可以用它来保存数据,最近面试的时候问到这个 List 和 那个 List 的区别是什么呀, Vector 和 ArrayList 的区别是什么呀,我连 Vector 是什么都不知道,我咋知道它俩有什么区别。。。唉,说来说去还是基础的锅,这不,连视频带看书,重新了解了一下 Java 的类集。
1、基本概念
Java 类集就是一组 Java 实现的数据结构,它是一个特别有用的工具类,它就像一个容器,把多个对象(其实是对象的引用)存到这个容器中。
一般来说,如果想要保存多个对象,可以用到对象数组,但是如果使用对象数组的话,长度是固定的,所以一般不用数组。也可以用链表来实现一个动态的对象数组,但是链表的开发很麻烦,链表的操作性能又比较差,而且链表还会使用 Object 类来保存对象,会涉及到向上转型和向下强制转型。于是乎,类集的优势就体现出来了。
Java 设计之初就提供了类似链表的工具类—— Vector ,但是后来发现这个类并不能很好的描述数据结构,所以从 Java 2 开始,提供了类集框架来实现数据结构,并在 Java 5 引入了泛型的概念,于是可以在类集框架中避免向下强制转型。在 Java 8 后还提供了数据流的操作。
2、Collection 接口
public boolean addAll(Collection<? extends E> c):将集合 c 中的所有元素添加到集合中,添加成功则返回 true。
public void clear():清空集合中的所有元素。
public boolean contains(Object o):判断集合中是否包含指定元素,如果包含则返回 true,否则返回 false。
public boolean containsAll(Collection<?> c):判断集合中是否包含集合 c 中的所有元素,如果包含则返回 true,否则返回 false。
public boolean isEmpty():判断集合是否为空,如果集合长度为 0 则返回true,否则返回 false。
public boolean remove(Object o):从集合中删除指定元素,如果集合中包含多个该元素时,只会删除第一个,删除成功则返回 true。
public boolean removeAll(Collection<?> c):从集合中删除集合 c 中的所有元素,删除成功则返回 true。
public int size():返回集合的长度,即元素个数。
public Object[] toArray():将集合转换成一个 Object 数组,所有的集合元素则变为对应的数组元素。
public <T> T[] toArray(T[] a):将集合转换为指定类型的数组。
public Iterator<E> iterator():返回 Iterator 对象,该对象用于遍历集合里的元素。
2.1 List 子接口
public E get(int index):返回指定索引的元素。
public int indexOf(Object o):返回指定元素第一次出现的索引。
public int lastIndexOf(Object o):返回指定元素最后一次出现的索引。
public ListIterator<E> listIterator():返回一个 ListIterator 对象。
public E remove(int index):删除并返回指定索引的元素。
public E set(int index,E element):设置(修改)指定索引的元素。
public List<E> subList(int fromIndex,int toIndex):返回从 fromIndex(包含)到 toIndex(不包含)的元素的子集合。
default void sort(Comparator<? super E> c):使用 Comparator 对集合进行排序。这个方法后面不做记录,以后有时间专门记录下各个排序方式。
2.1.1 ArrayList
public class ListDemo {public static void main(String[] args) {List<String> list = new ArrayList<String>();System.out.println(list);list.add("迈克尔乔丹");list.add("科比布莱恩特");list.add("特雷西麦克格雷迪");list.add("阿伦艾弗森");System.out.println(list);System.out.println("list中是否包含'迈克尔乔丹':" + list.contains("迈克尔乔丹"));System.out.println("list中是否包含'勒布朗詹姆斯':" + list.contains("勒布朗詹姆斯"));list.remove("迈克尔乔丹");System.out.println(list);list.add(1, "凯文杜兰特");list.add(3, "凯文杜兰特");System.out.println(list);System.out.println("凯文杜兰特第一次出现的索引:" + list.indexOf("凯文杜兰特"));System.out.println("凯文杜兰特最后一次出现的索引:" + list.lastIndexOf("凯文杜兰特"));list.set(2, "雷阿伦");System.out.println(list);System.out.println(list.get(2));System.out.println("截取第二个元素到第四个元素的子集合:" + list.subList(2, 4));list.clear();System.out.println("list是否为空:" + list.isEmpty());}}
class Person {private String name;private int age;public Person(String name, int age) {super();this.name = name;this.age = age;}@Overridepublic String toString() {return "[姓名:" + name + ",年龄:" + age + "]";}}public class ListDemo {public static void main(String[] args) {List<Person> list = new ArrayList<Person>();list.add(new Person("李一", 22));list.add(new Person("张三", 23));list.add(new Person("王八", 24));System.out.println(list);list.remove(new Person("王八", 24));System.out.println(list);}}这个程序看起来应该是添加了三个对象又删除了一个,所以最后应该还剩两个对象才对,但是实际运行结果却是:
2.1.2 Vector
Vector 是一个古老的类,在 Java 1 的时候就有了,因为那个年代 Java 还没有提供系统的集合框架,所以 Vector 中的方法名都比较长,比如 addElement(E obj),实际上它和add(E e) 并没有什么区别。为了方便编程而简短了方法名,应运而生了类集框架,所以对集合的操作就有了新的标准,为了保留下 Vector 类,所以 Vector 类多实现了 List 接口,所以 Vector 中会有一些功能重复的方法,比如 addElement(E obj) 和 add(E e),在 Vector 中这两个方法都有。Vector 的使用跟 ArrayList 一样,因为它们依赖的是接口而不是具体类,所以只是具体子类不一样,用法完全一样:
public class ListDemo {public static void main(String[] args) {List<Person> list = new Vector<Person>();list.add(new Person("李一", 22));list.add(new Person("张三", 23));list.add(new Person("王八", 24));System.out.println(list);}}运行结果如下:
2.1.3 Stack
public E peek():返回栈顶第一个元素,但并不会让元素出栈。
public E pop():返回栈顶第一个元素,并让这个元素出栈。
public boolean empty():判断该栈是否为空。
public int search(Object o):返回指定元素的索引。
public class ListDemo {public static void main(String[] args) {Stack<Person> stack = new Stack<Person>();stack.push(new Person("李一", 22));stack.push(new Person("张三", 23));stack.push(new Person("王八", 24));System.out.println("栈是否为空:" + stack.empty());System.out.println(stack.peek());System.out.println(stack.pop());System.out.println(stack.pop());System.out.println(stack.pop());System.out.println(stack.pop());}}运行程序,打印结果如下:
可以看到调用 peek() 方法只会返回栈顶元素,并不会将元素出栈,这个栈里面只有三个元素,当我们出栈三次后再调用 pop() 方法就发现程序报错了:java.util.EmptyStackException,说明当前栈已经为空了,不允许再执行出栈操作了。
2.1.4 LinkedList
public class ListDemo {public static void main(String[] args) {LinkedList<Person> list = new LinkedList<Person>();list.offer(new Person("李一", 22));list.push(new Person("张三", 23));list.push(new Person("王八", 24));list.offer(new Person("赵四", 25));// 顺序应该是王八,张三,李一,赵四System.out.println(list);// 返回第一个元素但不删除该元素System.out.println(list.peekFirst());// 返回最后一个元素但不删除该元素System.out.println(list.peekLast());// 返回栈顶元素并将该元素出栈System.out.println(list.pop());// 返回最后一个元素并删除该元素System.out.println(list.pollLast());System.out.println(list);}}运行程序,打印结果如下:
2.1.5 List 接口小结
2.2 Set 接口
2.2.1 HashSet
public class SetDemo {public static void main(String[] args) {Set<String> set = new HashSet<String>();set.add("李一");set.add("张三");set.add("王八");set.add("王八");set.add("王八");System.out.println(set);}}运行程序,打印结果如下:
class Person {private String name;private int age;public Person(String name, int age) {super();this.name = name;this.age = age;}@Overridepublic String toString() {return "[姓名:" + name + ",年龄:" + age + "]";}}
public class SetDemo {public static void main(String[] args) {Set<Person> set = new HashSet<Person>();set.add(new Person("李一", 21));set.add(new Person("张三", 22));set.add(new Person("王八", 23));set.add(new Person("王八", 23));System.out.println(set);}}运行程序,打印结果如下:
奇怪的事情发生了,不能保存重复元素的 HashSet 集合却保存了两个“重复”的对象。这是因为 HashSet 判断两个元素相等的标准是通过 equals() 和 hashCode() 方法,只有两个方法都比较相等时才会认为是同一个对象。所以我们需要重写这两个方法(必须两个都重写),利用编译器自动生成即可:
class Person {private String name;private int age;public Person(String name, int age) {super();this.name = name;this.age = age;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + ((name == null) ? 0 : name.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Person other = (Person) obj;if (age != other.age)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;return true;}@Overridepublic String toString() {return "[姓名:" + name + ",年龄:" + age + "]";}}再次运行,发现重复元素没有了:
2.2.1 TreeSet
HashSet 是无序的,而 TreeSet 是有序的,如何排序的,先不要方,先来看看能不能像使用 HashSet 一样使用 TreeSet,先保存字符串:
public class SetDemo {public static void main(String[] args) {Set<String> set = new TreeSet<String>();set.add("李一");set.add("张三");set.add("王八");set.add("王八");System.out.println(set);}}打印如下:
这没有问题,一旦保存对象:
public class SetDemo {public static void main(String[] args) {Set<Person> set = new TreeSet<Person>();set.add(new Person("李一", 21));set.add(new Person("张三", 22));set.add(new Person("王八", 23));set.add(new Person("赵四", 23));System.out.println(set);}}
一运行,程序就报错了:
Exception in thread "main" java.lang.ClassCastException: com.qinshou.collectiondemo.Person cannot be cast to java.lang.Comparable
类型转换错误,Person类 不能转为 Comparable 类,这个 Comparable 类是什么,之前说 TreeSet 有序的,这个 Comparable 就是帮助 TreeSet 排序的比较器,String 类是实现了这个接口的,所以我们保存字符串才会没问题,现在我们要利用 TreeSet 来保存自定义的类,那么这个自定义的类也得实现 Comparable 接口并且覆写接口中的方法,先简单比较一下年龄:
@Overridepublic int compareTo(Person o) {// TODO Auto-generated method stubif (this.age > o.age) {return 1;} else if (this.age < o.age) {return -1;}return 0;}
重新运行程序,结果如下:
我们发现确实按照年龄,排序了,但是却少了“赵四”这个对象,为什么?原因是我们只比较了年龄,年龄相等的时候返回 0 ,所以我们知道了,当 compareTo() 方法返回 0 时, TreeSet 则认为是同一个对象,所以不会保存“重复”元素。所以还需要修改 compareTo() 方法:
@Overridepublic int compareTo(Person o) {// TODO Auto-generated method stubif (this.age > o.age) {return 1;} else if (this.age < o.age) {return -1;}return this.name.compareTo(o.name);}
运行程序,结果如下:
由此我们也发现了 TreeSet 的问题,类中的成员变量我们都需要去比较,当类中成员变量很多的时候,我们覆写 compareTo() 方法就特别麻烦,所以我们一般也不会使用 TreeSet。
2.2.3 Set接口小结
3、Map 接口
Collection 接口是单值保存,即每次保存的是一个对象,而 Map 接口则是保存一对对象,即 key-value 对。value 可以重复,但是 key 不允许重复,因为每一个 key 只能对应一个 value,是一对一的关系。通过指定 key 可以拿到唯一确定的 value。 Map 接口常用方法如下:public void clear():删除该 Map 中的所有 key-value 对。
public boolean containsKey(Object key):查询该 Map 中是否包含指定 key ,如果有则返回 ture ,否则返回 false 。
public boolean containsValue(Object value):查询该 Map 中是否包含指定 value (至少一个),如果有则返回 ture ,否则返回 false 。
public Set<Map.Entry<K,V>> entrySet():返回 Map 中包含 key-value 对组成的 Set 集合,每个元素都为 Map.Entry(Map 的内部类)。
public V get(Object key):根据指定 key 返回对应 value,如果该 Map 中不包含指定 key ,则返回 null。
public Set<K> keySet():返回该 Map 中所有 key 组成的 Set 集合。
public put(K key, V value):添加一个 key-value 对,如果该 key-value 对已存在,则覆盖原来的数据。
public V remove(Object key):删除指定的 key 对应的 key-value 对,如果该 key 不存在,则返回 null。
public Collection<V> values():返回该 Map 中所有 value 组成的 Collection。
Map 接口并不像 Collection 接口那样还有更具体的子接口,它的实现类也很多,比较常用的 HashMap 和 Hashtable。
3.1 HashMap
Map 存放数据的最终目的是为了数据的查找,就像字典一样,而 Collection 的目的是为了存放数据和输出数据。
还是利用 HashMap 子类来介绍一下 Map 的基本使用:
public class MapDemo {public static void main(String[] args) {Map<String, Integer> map = new HashMap<String, Integer>();map.put("一", 1);map.put("二", 2);map.put("三", 3);System.out.println(map);System.out.println("是否包含key '一':" + map.containsKey("一"));System.out.println("是否包含key '四':" + map.containsKey("四"));System.out.println("是否包含value '1':" + map.containsValue(1));System.out.println("是否包含value '4':" + map.containsValue(4));System.out.println(map.get("二"));for (String string : map.keySet()) {System.out.println("每一个key:" + string);}for (int i : map.values()) {System.out.println("每一个value:" + i);}map.remove("二");System.out.println(map);map.clear();System.out.println(map);}}运行程序,打印结果如下:
通过打印我们可以看出,HashMap 的存放都是无序的,只要带有 hash,都是无序的。
HashMap 的 key 和 value 都是可以为 null 的。
public class MapDemo {public static void main(String[] args) {Map<String, Integer> map = new HashMap<String, Integer>();map.put(null, 0);map.put("空", null);System.out.println(map);}}
运行程序,打印如下:
一般情况下,我们是使用 Integer 或者 String 类型的数据作为 key ,那么如果我们使用自定义类来作为 key 会发生什么情况呢?先创建一个 Person 类:
class Person {private String name;private int age;public Person(String name, int age) {super();this.name = name;this.age = age;}@Overridepublic String toString() {return "[姓名:" + name + ",年龄:" + age + "]";}}
我们来看看分别使用 String 和 自定义 Person 类作为 key 时的不同情况:
public class MapDemo {public static void main(String[] args) {Map<String, Person> map = new HashMap<String, Person>();map.put("李一", new Person("李一", 21));System.out.println(map.get("李一"));Map<Person, String> map2 = new HashMap<Person, String>();map2.put(new Person("李一", 21), "李一");System.out.println(map2.get(new Person("李一", 21)));}}运行程序,打印结果如下:
可以看到使用 Person 类作为 key 的时候,取出时为 null 。Map 中有一个方法 keySet() 是获得所有 key 的,返回的是一个 Set 集合,就是说 key 是存放在 Set 集合中的,前面提到 Set 集合判断重复元素依靠的是 hashCode() 和 equals() 方法,所以我们如果要使用自定义类作为 key 的话,必须要覆写这两个方法。一般情况下,首选都是使用 String 类作为 key。
3.2 Hashtable
Hashtable 是一个古老的实现类,从 Java 1 就出现了,所以它跟 Vector 类似,里面也有几个方法名比较长的方法,它的使用跟 HashMap 完全一样,因为之前说过,它们依赖的是接口。但是它和 HashMap 不同的是它不允许 key 或 value 为null。
public class MapDemo {public static void main(String[] args) {Map<String, Integer> map = new Hashtable<String, Integer>();map.put("一", 1);map.put("二", 2);map.put("三", 3);System.out.println(map);map.put(null, 0);map.put("空", null);}}运行程序,打印结果如下:
当保存 null 的时候抛出了 NullPointerException ,这就是 Hashtable 和 HashMap 的不同。
3.3 Map 接口小结
4、总结
- Java 基础之类集
- java基础之类继承
- java基础之类继承
- java基础之类
- java基础之类与对象
- Java基础之类和对象
- Java基础之类与对象
- Java基础之类和对象
- Java基础之类和对象
- Java基础之类与对象
- java基础之类加载器
- java基础之类加载过程
- java基础之类和对象
- Java基础之类与对象
- 黑马程序员——java基础之类集框架
- JAVA面试基础(代码之类)
- Java基础加强之类加载器
- Java基础加强之类加载器
- HDU2093 考试排名(排序)
- Idea数据库操作(Full Join)
- ns3之PCAP tracing文件命名格式
- 全面总结Java的GC算法和回收机制
- Java单例模式实现方式
- Java 基础之类集
- 中断处理函数的注意事项
- css 屏幕自适应
- 面向对象之关键字final
- Android学习笔记——Intent
- 百度站内搜索代码
- java list 交集 并集 差集 去重复并集
- ISP-坏点校正(DPC)
- Java设计模式之创建型模式--工厂方法及抽象工厂模式