我有以下代码用于显示ArrayList的两个连续元素的总和,直到剩下的元素为一。例如: -
如果我进入
1 2 3 4 5
输出
3 7 5 //添加两个连续的最后一个是原样的
10 5 //做同样的事情
15个
代码
import java.util.*;
import java.lang.Integer;
class Substan{
ArrayList <Integer> list = new ArrayList <Integer> ();
ArrayList <Integer> newList = new ArrayList <Integer> ();// this will be the list containing the next sequence.
int index=0;
int sum=0;
Substan(){
Scanner read = new Scanner(System.in);
String choice;
System.out.println("Enter the elements of the array");
do{
int element = read.nextInt();
list.add(element);
System.out.println("More?");
choice = read.next();
}while(choice.equals("y") || choice.equals("Y"));
}
/* precondition- we have the raw list that user has enterd.
postcondition - we have displayed all the sublists,by adding two consecutives numbers and the last one is having one element.
*/
void sublist(){
while(noofElementsIsNotOneInList()){
index =0;
while(newListIsNotComplete()){
if(nextElementIsThere()){
sum = addTheConsecutive();
}
else{
sum = getLastNumber();
}
storeSumInNewList();
}
displayTheNewList();
System.out.println("");
updateTheLists();
}
displayTheNewList(); //as we have danger of Off By One Bug (OBOB)
System.out.println("");
}
private boolean noofElementsIsNotOneInList(){
boolean isnotone = true;
int size = list.size();
if ( size == 1){
isnotone = false;
}
return isnotone;
}
private boolean newListIsNotComplete(){
boolean isNotComplete = true;
int listSize = list.size();
int newListSize = newList.size();
if (listSizeIsEven()){
if ( newListSize == listSize/2){
isNotComplete = false;
}
}
else{
if( newListSize == (listSize/2) +1){
isNotComplete = false;
}
}
return isNotComplete;
}
private boolean listSizeIsEven(){
if ( list.size()%2 == 0 ){
return true;
}
else{
return false;
}
}
/*
we are at some index.
returns true if we have an element at (index+1) index.
*/
private boolean nextElementIsThere(){
if ( list.size() == index+1 ){
return false;
}
else{
return true;
}
}
/* precondition-we are at index i
postcondition - we will be at index i+2 and we return sum of elements at index i and i+1.
*/
private int addTheConsecutive(){
int sum = list.get(index)+list.get(index+1);
index += 2;
return sum;
}
/* we are at last element and we have to return that element.
*/
private int getLastNumber(){
return list.get(index);
}
private void storeSumInNewList(){
newList.add(sum);
}
private void displayTheNewList(){
int size = newList.size();
for ( int i=0;i<size;i++){
System.out.print(newList.get(i)+" ");
}
}
/*precondition - we have processed all the elements in the list and added the result in newList.
postcondition - Now my list will be the newList,as we are processing in terms of list and newList reference will have a new object.
*/
private void updateTheLists(){
list = newList;
newList = new ArrayList <Integer>();// changing the newList
}
public static void main(String[] args) {
Substan s = new Substan();
s.sublist();
}
}
所以我已经对我的代码做了很多改进,但是遇到了与其他方法共享局部变量的问题。例如我使用index
实例来存储索引,最初我认为我会把它作为一个实例而不是方法sublist()
中的局部变量,但是因为无法从需要使用index
addTheConsecutive()
的其他方法中查看它。所以考虑到我放了{ {1}}在类级别。所以,是否需要在类级别共享变量,而不是仅在编码之前查看对象的状态并坚持使用并且永远不会更改它?“#/ p>
答案 0 :(得分:2)
考虑一下:
对象只能通过共享其属性与其他对象进行通信。所以,如果你需要一个对象来读取另一个对象的状态,唯一可以做到的就是给它&#34;权限&#34;阅读其他对象属性。
您有两种方法可以做到这一点:
public
或getXXX()
方法(对私有属性有意义)我个人更喜欢选项二,因为getXXX()
方法返回特定属性的值(&#34; state&#34;)而没有被修改的风险。当然,如果您需要修改私有属性,还应该编写setXXX()
方法。
示例:强>
public class MyClass {
private int foo;
private String bar;
/*
* Code
*/
public int getFoo() {
return foo;
}
public String getBar() {
return bar;
}
public void setFoo(int foo) {
this.foo = foo;
}
public void setBar(String bar) {
this.bar = bar;
}
/*
* More code
*/
}
这样封装了所有对象属性,并且:
getXXX()
函数,否则任何其他对象都无法读取它们,setXXX()
函数。答案 1 :(得分:2)
将其与非抽象版本进行比较。
for (int index = 0; index < list.size(); index += 2) {
int sum = list.get(index);
if (index + 1 < list.size() {
sum += list.get(index + 1);
}
newList.add(sum);
}
现在,使用名称自上而下精炼算法是一种合理的方法论,有助于进一步的创意编程。
可以看出,当再次抽象出上述内容时:
while (stillNumbersToProcess()) {
int sum = sumUpto2Numbers();
storeSumInNewList(sum);
}
可以将许多变量(如sum
)保留为局部变量,从而简化状态。
一种有用的抽象是条件的使用,以更直接的形式:
private boolean listSizeIsEven() {
return list.size() % 2 == 0;
}
private boolean nextElementIsThere() {
return index + 1 < list.size();
}
答案 2 :(得分:1)
在Class级别声明index
没有意义,因为您不希望它成为该类的成员或实例。而是将其作为方法的本地,并将其作为参数传递给其他方法,以便访问它。
答案 3 :(得分:1)
我认为你问的是错误的问题。
你的类变量很有意义,许多方法也是如此。这主要是因为:
你所做的类变量更有意义作为方法参数传递。有些方法需要看到它们,有些则不需要。
你的课也有点奇怪,因为在同一课上两次打电话subList
不会产生相同的答案。
代码中充斥着我不太注意的方法,例如:
private boolean noofElementsIsNotOneInList(){
boolean isnotone = true;
int size = list.size();
if ( size == 1){
isnotone = false;
}
return isnotone;
}
不应该是:
private boolean noofElementsIsNotOneInList(){
return list.size() == 1;
}
使用一些任意List
并传递一个是没有意义的,这样你就知道你正在检查哪个List
:
private boolean noofElementsIsNotOneInList(final Collection<?> toCheck){
return toCheck.size() == 1;
}
同样的逻辑可以应用于几乎所有的方法。
这将删除实例变量并使您的代码更具可读性。
TL; DR :使用大量适当命名的方法:好。让那些方法做一些人们不会想到的事情:糟糕。有很多冗余的代码使得事情很难阅读:糟糕。
实际上,只是为了证明一点,整个类(除了从stdin读取的逻辑,不管怎么说都不应该存在)可以转换成一个简短的递归方法,它根本不需要实例变量:
public static int sumPairs(final List<Integer> list) {
if (list.size() == 1)
return list.get(0);
final List<Integer> compacted = new LinkedList<>();
final Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
final int first = iter.next();
if (iter.hasNext()) compacted.add(first + iter.next());
else compacted.add(first);
}
return sumPairs(compacted);
}
现在你可以把这个方法分成几个适当命名的更短的方法,这是有道理的。从另一端开始有时更有帮助。勾勒出代码的逻辑和它正在尝试做的事情,然后找到有意义的片段将其拆分成。可能在添加单元测试以验证行为之后。
答案 4 :(得分:1)
通过递归做什么:
public int calculateSum(List<Integer> nums) {
displayList(nums);
if (nums.size() == 1) {
return nums.get(0);
}
List<Integer> interim = new ArrayList<Integer>();
for (int i = 0; i < nums.size(); i = i + 2) {
if (i + 1 < nums.size()) {
interim.add(nums.get(i) + nums.get(i + 1));
} else {
interim.add(nums.get(i));
}
}
return calculateSum(interim);
}
public static void displayList(List<Integer> nums){
System.out.println(nums);
}
步骤:
Run calculate sum until list has 1 element
if list has more than 1 element:
iterate the list by step +2 and sum the element and put into a new List
again call calculate sum