在编写JAVA程序中,我们经常会遇到需要保存一组数据对象,此时,我们可以采用对象数组来进行多个对象的保存,但对象数组存在一个最大的问题即在于长度上的限制,如果说我们现在要保存一组对象,但是我们并知道数组对象到底有多少个的时候,那么此时就遇到了困难,因此为了解决此问题,在JDK1.2中,提出了类集框架的概念,并在JDK1.5中对此框架进行了修改,加入了泛型的支持,从而保证了操作的安全性。而在整个集合中,提供了几个集合核心操作的接口,分别为:Collection、Set、List、Enumeration、Iterator、ListIterator等。

  1)单值保存的最大父接口:Collection

所谓的单值保存指的是每次操作只保存一个对象,每次增加只增加一个对象,而在Collection接口之中定义了如下几个常用的方法:

  1. package com.njupt.study.collection;
  2.  
  3. import java.util.Iterator;
  4.  
  5. public interface Collection<E> extends Iterable<E>{
  6.  
  7. /**
  8. * 增加数据
  9. * @param e
  10. * @return
  11. */
  12. boolean add(E e);
  13. /**
  14. * 删除指定的元素
  15. * @param o
  16. * @return
  17. */
  18. boolean remove(Object o);
  19. /**
  20. * 清除数据
  21. */
  22. void clear();
  23. /**
  24. * 判断集合是否为空
  25. * @return
  26. */
  27. boolean isEmpty();
  28. /**
  29. * 获取元素的个数
  30. * @return
  31. */
  32. int size();
  33. /**
  34. * 查找一个数据是否存在
  35. * @param o
  36. * @return
  37. */
  38. boolean contains(Object o);
  39. /**
  40. * 将集合变为对象数组后返回
  41. * @return
  42. */
  43. Object[] toArray();
  44. /**
  45. * 将集合变为指定类型的对象数组
  46. * @param <T>
  47. * @param a
  48. * @return
  49. */
  50. <T> T[] toArray(T[] a);
  51. /**
  52. * 为Iterator接口实例化,来源于父类接口Iterable
  53. */
  54. Iterator<E> iterator();
  55.  
  56. }

  Collection接口本身在开发之中并不会直接的去使用,而在开发之中往往会使用两个子接口:List、Set。

  2)允许重复的子接口:List

List接口是Collection接口的子接口,是有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

  public interface List<E>extends Collection<E>

List接口对Collection接口进行了大量的扩充操作,而主要的扩充方法有如下几个:

  

  1. public interface List<E> extends Collection<E> {
  2.  
  3. /**
  4. * 返回指定位置上的数据
  5. * @param index
  6. * @return
  7. */
  8. E get(int index);
  9.  
  10. /**
  11. * 修改指定位置上的数据
  12. * @param index
  13. * @param element
  14. * @return
  15. */
  16. E set(int index, E element);
  17.  
  18. /**
  19. * 为ListIterator接口实例化
  20. * @return
  21. */
  22. ListIterator<E> listIterator();
  23. }

  List本身也是一个接口,所以如果要想使用这个接口就必须有子类,实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

  1. ArrayList:

在List接口里面ArrayList子类的使用几率是最高的,一般List接口实例化,一般想到的为ArrayList。

  1. package com.njupt.study.collection;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. class Student
  7. {
  8. private String name;
  9.  
  10. private int age;
  11.  
  12. public Student(){};
  13.  
  14. public Student(String n,int a)
  15. {
  16. this.name = n;
  17. this.age = a;
  18. }
  19.  
  20. public String getName() {
  21. return name;
  22. }
  23.  
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27.  
  28. public int getAge() {
  29. return age;
  30. }
  31.  
  32. public void setAge(int age) {
  33. this.age = age;
  34. }
  35.  
  36. }
  37.  
  38. public class Demo02 {
  39.  
  40. public static void main(String[] args) {
  41. List<Student> list = new ArrayList<Student>();
  42.  
  43. list.add(new Student("zhangsan",18));
  44.  
  45. list.add(new Student("lisi",19));
  46.  
  47. list.add(new Student("wangwu",17));
  48.  
  49. System.out.println(list);
  50.  
  51. System.out.println("******************");
  52.  
  53. for(int i=0;i<list.size();i++)
  54. {
  55. System.out.println(list.get(i));
  56. }
  57. }
  58. }

输出结果为:

[com.njupt.study.collection.Student@1fc4bec, com.njupt.study.collection.Student@dc8569, com.njupt.study.collection.Student@1bab50a]
******************
com.njupt.study.collection.Student@1fc4bec
com.njupt.study.collection.Student@dc8569
com.njupt.study.collection.Student@1bab50a

为了显示更加明显的信息,因此增加重写toString()方法,

public String toString()
{
return this.name+"---->"+this.age;
}

显示结果为:

[zhangsan---->18, lisi---->19, wangwu---->17]
******************
zhangsan---->18
lisi---->19
wangwu---->17

可以看见ArrayList采用的为数组方式保存元素对象。

add 为添加元素,那么remove 可以删除元素,那么我们试试删除元素:

  1. package com.njupt.study.collection;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. class Student
  7. {
  8. private String name;
  9.  
  10. private int age;
  11.  
  12. public Student(){};
  13.  
  14. public Student(String n,int a)
  15. {
  16. this.name = n;
  17. this.age = a;
  18. }
  19.  
  20. public String getName() {
  21. return name;
  22. }
  23.  
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27.  
  28. public int getAge() {
  29. return age;
  30. }
  31.  
  32. public void setAge(int age) {
  33. this.age = age;
  34. }
  35.  
  36. public String toString()
  37. {
  38. return this.name+"---->"+this.age;
  39. }
  40. }
  41.  
  42. public class Demo02 {
  43.  
  44. public static void main(String[] args) {
  45. List<Student> list = new ArrayList<Student>();
  46.  
  47. list.add(new Student("zhangsan",18));
  48.  
  49. list.add(new Student("lisi",19));
  50.  
  51. list.add(new Student("wangwu",17));
  52.  
  53. System.out.println(list);
  54.  
  55. System.out.println("******************");
  56.  
  57. for(int i=0;i<list.size();i++)
  58. {
  59. System.out.println(list.get(i));
  60. }
  61.  
  62. System.out.println("*********删除元素*************");
  63.  
  64. list.remove(new Student("wangwu",17));
  65. for(int i=0;i<list.size();i++)
  66. {
  67. System.out.println(list.get(i));
  68. }
  69.  
  70. }
  71. }

输出结果为:

[zhangsan---->18, lisi---->19, wangwu---->17]
******************
zhangsan---->18
lisi---->19
wangwu---->17
*********删除元素*************
zhangsan---->18
lisi---->19
wangwu---->17

可以看见元素并没有被删除,为什么呢?  那我们试试非自定义对象是否可以删除呢?

  1. package com.njupt.study.collection;
  2.  
  3. import java.util.ArrayList;
  4.  
  5. public class Demo01 {
  6.  
  7. /**
  8. * @param args
  9. */
  10. public static void main(String[] args) {
  11. java.util.List<String> list = new ArrayList<String>();
  12.  
  13. list.add("a");
  14.  
  15. list.add("b");
  16.  
  17. list.add("c");
  18.  
  19. System.out.println(list);
  20.  
  21. for (int x = 0; x < list.size(); x++) {
  22. System.out.println(list.get(x));
  23. }
  24.  
  25. list.remove("c");
  26.  
  27. System.out.println("**********************");
  28.  
  29. for (int x = 0; x < list.size(); x++) {
  30. System.out.println(list.get(x));
  31. }
  32. }
  33.  
  34. }

显示结果为:

[a, b, c]
a
b
c
**********************
a
b

我们发现是可以删除的,为什么我们自己定义的不能删除呢?我们看下String的源码有什么不同?

  1. public boolean equals(Object anObject) {
  2. if (this == anObject) {
  3. return true;
  4. }
  5. if (anObject instanceof String) {
  6. String anotherString = (String)anObject;
  7. int n = count;
  8. if (n == anotherString.count) {
  9. char v1[] = value;
  10. char v2[] = anotherString.value;
  11. int i = offset;
  12. int j = anotherString.offset;
  13. while (n-- != 0) {
  14. if (v1[i++] != v2[j++])
  15. return false;
  16. }
  17. return true;
  18. }
  19. }
  20. return false;
  21. }

  原因应该就是上面的,因为对象在删除的时候,需要判断集合中的对象和要删除的对象是否一致,然后才能删除,但我们自定义对象中,并没有重写equals方法所以,删除的时候没有成功,为此,我们增加equals方法试试如何?

  1. public boolean equals(Object obj)
  2. {
  3. if(this == obj)
  4. {
  5. return true;
  6. }
  7.  
  8. if(obj == null)
  9. {
  10. return false;
  11. }
  12.  
  13. if( ! (obj instanceof Student) )
  14. {
  15. return false;
  16. }
  17.  
  18. Student other = (Student) obj;
  19.  
  20. if(this.name.equals(other.name) && this.age == other.age)
  21. {
  22. return true;
  23. }
  24. return false;
  25. }

显示结果为:

[zhangsan---->18, lisi---->19, wangwu---->17]
******************
zhangsan---->18
lisi---->19
wangwu---->17
*********删除元素*************
zhangsan---->18
lisi---->19

已成功删除。

注意:

既然ArrayList类可以为List接口实例化,那么也就可以为Collection接口实例化,但是这个时候已经不可以继续使用get()方法操作了,所以此时只能够将Collection变为对象数组后返回。

  1. package com.njupt.study.collection;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Collection;
  5. import java.util.List;
  6.  
  7. class Student
  8. {
  9. private String name;
  10.  
  11. private int age;
  12.  
  13. public Student(){};
  14.  
  15. public Student(String n,int a)
  16. {
  17. this.name = n;
  18. this.age = a;
  19. }
  20.  
  21. public String getName() {
  22. return name;
  23. }
  24.  
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28.  
  29. public int getAge() {
  30. return age;
  31. }
  32.  
  33. public void setAge(int age) {
  34. this.age = age;
  35. }
  36.  
  37. public boolean equals(Object obj)
  38. {
  39. if(this == obj)
  40. {
  41. return true;
  42. }
  43.  
  44. if(obj == null)
  45. {
  46. return false;
  47. }
  48.  
  49. if( ! (obj instanceof Student) )
  50. {
  51. return false;
  52. }
  53.  
  54. Student other = (Student) obj;
  55.  
  56. if(this.name.equals(other.name) && this.age == other.age)
  57. {
  58. return true;
  59. }
  60. return false;
  61. }
  62.  
  63. /*public boolean equals(Object obj) {
  64. if (this == obj) {
  65. return true ;
  66. }
  67. if (obj == null) {
  68. return false ;
  69. }
  70. if (! (obj instanceof Student)) {
  71. return false ;
  72. }
  73. Student t = (Student) obj ;
  74. if(this.name.equals(t.name ) && this.age == t.age) {
  75. return true ;
  76. }
  77. return false ;
  78. } */
  79.  
  80. public String toString()
  81. {
  82. return this.name+"---->"+this.age;
  83. }
  84. }
  85.  
  86. public class Demo02 {
  87.  
  88. public static void main(String[] args) {
  89. Collection<Student> list = new ArrayList<Student>();
  90.  
  91. list.add(new Student("zhangsan",18));
  92.  
  93. list.add(new Student("lisi",19));
  94.  
  95. list.add(new Student("wangwu",17));
  96.  
  97. System.out.println(list);
  98.  
  99. System.out.println("******************");
  100.  
  101. Student[] all = list.toArray(new Student[]{});
  102.  
  103. for(int i=0;i<all.length;i++)
  104. {
  105. System.out.println(all[i]);
  106. }
  107.  
  108. }
  109. }

2. LinkedList

同样实现List接口的LinkedList与ArrayList不同,ArrayList是一个动态数组,而LinkedList是一个双向链表。所以它除了有ArrayList的基本操作方法外还额外提供了insert方法在LinkedList的首部或尾部。

由于实现的方式不同,LinkedList不能随机访问,它所有的操作都是要按照双重链表的需要执行。在列表中索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。这样做的好处就是可以通过较低的代价在List中进行插入和删除操作。

与ArrayList一样,LinkedList也是非同步的。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List: 
List list = Collections.synchronizedList(new LinkedList(…));

ArrayList擅长于随机访问。同时ArrayList是非同步的。

3. Vector

Vector类是在JDK 1.0的时候所推出的最早的实现数据结构支持的类,其最早翻译为向量,但是到了JDK 1.2之后,为了使其可以继续使用,所以让这个类多实现了一个List接口,这样一来,就造成了Vector子类的操作方法比ArrayList更多,但是一般这些多的方法很少考虑。

与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。

4.Stack

Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

ArrayList和Vector的区别:

ArrayList和Vector都属于List接口的常用子类,这两者在操作形式以及概念上的区别如下:

No.

区别点

ArrayList

Vector

1

推出时间

JDK 1.2时推出,属于新的类

JDK 1.0时推出,属于旧的类

2

性能

使用异步处理方式,性能较高

采用同步处理操作,性能相对较低

3

安全性

非线程安全

线程安全

4

输出

因为Java主要从事于网络的开发,所以使用异步的处理操作形式要比使用同步(Synchronized)更多。

  3)不允许重复的子接口:Set

      Set接口与List接口最大的不同在于里面的数据不允许有重复,那么首先观察一下Set接口的继承结构:

  1. public interface Set<E>
  1. extends Collection<E>

Set是一种不包括重复元素的Collection,与List一样,它同样运行null的存在但是仅有一个。由于Set接口的特殊性,所有传入Set集合中的元素都必须不同,同时要注意任何可变对象,如果在对集合中元素进行操作时,导致e1.equals(e2)==true,则必定会产生某些问题。实现了Set接口的集合有:EnumSet、HashSet、TreeSet。

1.EnumSet

是枚举的专用Set。所有的元素都是枚举类型。

2.HashSet

哈希(Hash)是一种数据的排列算法,这种算法的典型操作就是加塞算法,那块有地就保存,所以只要带有hash都是无序的。HashSet堪称查询速度最快的集合,因为其内部是以HashCode来实现的。它内部元素的顺序是由哈希码来决定的,所以它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。

  1. package com.njupt.study.collection;
  2.  
  3. import java.util.HashSet;
  4. import java.util.Set;
  5.  
  6. public class Demo03 {
  7.  
  8. /**
  9. * @param args
  10. */
  11. public static void main(String[] args) {
  12. Set<String> all = new HashSet<String>();
  13. all.add("Hello");
  14. all.add("World");
  15. all.add("Hello"); // 重复数据
  16. System.out.println(all);
  17. }
  18.  
  19. }

输出结果为:

[World, Hello]

通过本程序可以发现,里面所保存的顺序改变,而且如果有重复的数据也不能够保存。

3.TreeSet

在TreeSet子类里面所有保存的数据是没有重复的,而且可以为用户自动的进行排序。基于TreeMap,生成一个总是处于排序状态的set,内部以TreeMap来实现。它是使用元素的自然顺序对元素进行排序,或者根据创建Set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

关于排序的说明:既然在TreeSet中的所有数据都可以排序,而且里面设置的数据也都是对象,那么下面就使用自定义类完成。

但是这个时候必须注意到一点:对于现在的TreeSet子类而言,由于其要对一组对象进行排序,那么这个对象所在的类就一定要实现Comparable接口,用于指定排序规则;

  1. package com.njupt.study.collection;
  2.  
  3. import java.util.Set;
  4. import java.util.TreeSet;
  5.  
  6. class Student implements Comparable<Student>
  7. {
  8. private String name;
  9.  
  10. private int age;
  11.  
  12. public Student(){};
  13.  
  14. public Student(String n,int a)
  15. {
  16. this.name = n;
  17. this.age = a;
  18. }
  19.  
  20. public String getName() {
  21. return name;
  22. }
  23.  
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27.  
  28. public int getAge() {
  29. return age;
  30. }
  31.  
  32. public void setAge(int age) {
  33. this.age = age;
  34. }
  35.  
  36. public boolean equals(Object obj)
  37. {
  38. if(this == obj)
  39. {
  40. return true;
  41. }
  42.  
  43. if(obj == null)
  44. {
  45. return false;
  46. }
  47.  
  48. if( ! (obj instanceof Student) )
  49. {
  50. return false;
  51. }
  52.  
  53. Student other = (Student) obj;
  54.  
  55. if(this.name.equals(other.name) && this.age == other.age)
  56. {
  57. return true;
  58. }
  59. return false;
  60. }
  61.  
  62. public String toString()
  63. {
  64. return this.name+"---->"+this.age;
  65. }
  66.  
  67. @Override
  68. public int compareTo(Student o) {
  69. if (this.age > o.age) {
  70. return 1;
  71. } else if (this.age < o.age) {
  72. return -1;
  73. } else {
  74. return this.name.compareTo(o.name);
  75. }
  76. }
  77. }
  78.  
  79. public class Demo04 {
  80.  
  81. /**
  82. * @param args
  83. */
  84. public static void main(String[] args) {
  85. Set<Student> all = new TreeSet<Student>();
  86. all.add(new Student("张三",20)) ;
  87. all.add(new Student("李四",19)) ;
  88. all.add(new Student("王五",22)) ; // 年龄一样
  89. all.add(new Student("赵六",22)) ; // 年龄一样
  90. all.add(new Student("孙七",25)) ;
  91. all.add(new Student("孙七",25)) ; // 插入了重复的数据
  92. all.remove(new Student("李四",19)) ; // 删除一个数据
  93. System.out.println(all);
  94. }
  95.  
  96. }

显示结果为:[张三---->20, 王五---->22, 赵六---->22, 孙七---->25]

需要注意:

实现了对自定义类对象的排序,并且可以判断重复元素,但是TreeSet类只是利用了Comparable完成了重复元素的判断,可是这种判断并不是真正意义上的重复元素判断。

如果说现在要想判断一个对象是否重复,严格来讲,这个操作是由Object类所提供的,在任何一个子类里面需要覆写Object类中的以下两个方法;

· 对象比较:public boolean equals(Object obj);

· 对象编码:public int hashCode();

hashCode()方法是用于进行对象编码计算的操作方法,如果要想进行对象的编码,那么肯定需要一些数学上的支持,这里就不详细讲解了。

JAVA集合一之集合简介(Collection,List,Set)的更多相关文章

  1. Java中的集合(六)继承Collection的Set接口

    Java中的集合(六)继承Collection的Set接口 一.Set接口的简介 Set接口和List接口都是继承自Collection接口,它与Collection接口中功能基本一致,并没有对Col ...

  2. Java中的集合(五)继承Collection的List接口

    Java中的集合(五)继承Collection的List接口 一.List接口简介 List是有序的Collection的,此接口能够精确的控制每个元素插入的位置.用户能够根据索引(元素在List接口 ...

  3. Java集合【6.1】-- Collection接口源码详解

    目录 一.Collection接口简介 二.Collection源码分析 三.Collection的子类以及子类的实现 3.1 List extend Collection 3.2 Set exten ...

  4. Java基础知识强化之集合框架笔记06:Collection集合存储自定义对象并遍历的案例

    1.练习:用集合存储5个学生对象,并把学生对象进行遍历. 分析: (1)创建学生类(2)创建集合对象(3)创建学生对象(4)把学生添加到集合(5)把集合转成数组(6)遍历数组 2. 代码示例: Stu ...

  5. Java集合总结系列2:Collection接口

    Collection 接口是 Java 集合类的一个根接口,Java 在 Collection 接口中定义了许多通用的数据操作类方法以及判断类方法. 通过查看 API 文档或源码的方式,我们可以了解到 ...

  6. Java集合框架(一)—— Collection、Iterator和Foreach的用法

    1.Java集合概述 在编程中,常常需要集中存放多个数据.当然我们可以使用数组来保存多个对象.但数组长度不可变化,一旦在初始化时指定了数组长度,则这个数组长度是不可变的,如果需要保存个数变化的数据,数 ...

  7. JAVA之旅(十八)——基本数据类型的对象包装类,集合框架,数据结构,Collection,ArrayList,迭代器Iterator,List的使用

    JAVA之旅(十八)--基本数据类型的对象包装类,集合框架,数据结构,Collection,ArrayList,迭代器Iterator,List的使用 JAVA把完事万物都定义为对象,而我们想使用数据 ...

  8. Java学习关于集合框架的基础接口--Collection接口

     集合框架(Collection  Framework)是Java最强大的子系统之一,位于java.util 包中.集合框架是一个复杂的接口与和类层次,提供了管理对象组的最新技术.Java集合框架标准 ...

  9. Java学习:单列集合Collection

    集合 学习集合的目标: 会使用集合存储数据 会遍历集合,把数据取出来 掌握每种集合的特性 集合和数组的区别 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的元素,可以存储基本数据类型 ...

  10. Java中的集合(上):概述、Collection集合、List集合及其子类

    一.集合的体系结构 二.Collection集合 1.基本使用 如下代码 import java.util.ArrayList; import java.util.Collection; public ...

随机推荐

  1. 长平狐 Cocos2d-x 的“HelloWorld” 深入分析

                              Cocos2d-x 的“HelloWorld” 深入分析 本节所用Cocos2d-x版本:cocos2d-1.0.1-x-0.12.0 不能免俗,一 ...

  2. Chapter5 – 碰撞检测

    主人公能够放子弹了,虽然子弹看起来很美,但是怎么样来打到妖怪? 在这一章我们介绍一下最简单的碰撞检测方法去实现它. 首先第一个,我们有必要保存每个妖怪和子弹的指针,来够追踪他们的位置. 在这个游戏中我 ...

  3. C语言-break和continue

    先看以下switch语句的程序:     scanf("%d",&score);     if (score>=0 && score<=100) ...

  4. Unity3D ——强大的跨平台3D游戏开发工具(三)

    第四章 为地形添加水源.水流以及水下的模糊效果 制作好了地形的各种效果,接下来我们给场景添加一些水效果,使场景更加丰富. 第一步:添加水面 由于我在上一次的地形创作中就已经在山峰之间制作了一块洼地,它 ...

  5. Linux文件编辑之sed命令

    文件编辑之sed命令 sed是一种流编辑器,它是文本处理中非常重要的工具,能够完美配合正则表达式使用,功能不同凡响.处理时,把当前处理的行存储在临时缓冲区中,称为模式空间 (pattern space ...

  6. 1.3.1. 新建Xcode项目并设置故事板(Core Data 应用程序实践指南)

    创建名为Grocery Dude的Single View程序,并按默认设置处理,不勾选Core Date 和 Git. 设计故事板: 选择Main.Storyboard 拖放一个 Table View ...

  7. YII 1.0 扩展第三方类

    扩展缩略图类在blog\protected\extensions 中建立 Image/CThumb.php 1. 自己瞎弄的,一点都不优雅 include_once Yii::app()->Ba ...

  8. db2 数据类型

    数据类型: 字符串类型 描述 CHARACTER(n) n bytes定长字符串. n 大于0 不大于255. 默认 1. VARCHAR(n) 变长字符串,最大 n bytes. n大于 0 小于表 ...

  9. HTML 表单元素、 输入类型、Input 属性

    <input> 元素 最重要的表单元素是 <input> 元素. <input> 元素根据不同的 type 属性,可以变化为多种形态. 注释:下一章讲解所有 HTM ...

  10. spring mvc redirect设置FlashAttribute

    在Controller中设置: @RequestMapping("/redir") public String redir(Model model, RedirectAttribu ...