0

I have the following code for displaying the sum of two consecutive element of ArrayList until the element left is one.for example:-
if i entered

1 2 3 4 5

output
3 7 5 //adding the two consecutive last one is as it is
10 5//doing the same thing
15
code

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();
    }
}     

So i have done a lot of refinement of my code but having a problem of sharing the local variables with the other methods.for example i have used index instance for storing the index and initially i thought that i will put this as not an instance but a local variable in method sublist() but as it cannot be viewed from other methods which needed to use the index like addTheConsecutive().So considering that i put the index at class level.So is it wright approach that put the variables that are shared at class level rather than looking at only the state of the object initially before coding and stick to that and never change it?

OldSchool
  • 2,123
  • 4
  • 23
  • 45
  • 1
    Sharing indexes of arrays between methods sounds a bit weird but if you need to do it better do it by passing the index to the method as an argument (and if it changes - pass it back as a returned value). I don't see any added value in declaring it as a class member variable of as static. – Nir Alfasi Jun 22 '14 at 08:29
  • @alfasin so this is not the good approach to share the index – OldSchool Jun 22 '14 at 08:31

5 Answers5

2

Consider this:

An object can communicate with other(s) only by sharing its attributes. So, if you need an object to read the state of another, the only way it can be done is by giving it "permission" to read the other object attributes.

You have two ways to do that:

  1. Declaring the object attributes public, or
  2. Creating getXXX() methods (makes sense for private attributes)

I personally prefer option two, because the getXXX() method returns the value ("state") of a particular attribute without the risk of being modified. Of course, if you need to modify a private attribute, you should also write a setXXX() method.

Example:

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
     */
} 

This way all the object attributes are encapsulated, and:

  1. they cannot be read by any other object, unless you specifically call the appropriate getXXX() function, and
  2. cannot be altered by other objects, unless you specifically call the appropriate setXXX() function.
Barranka
  • 20,547
  • 13
  • 65
  • 83
2

Compare it with the non-abstracted version.

        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);
        }

Now, top-down refining the algorithm using names is a sound methodology, which helps in further creative programming.

As can seen, when abstracting the above again:

        while (stillNumbersToProcess()) {
            int sum = sumUpto2Numbers();
            storeSumInNewList(sum);
        }

One may keep many variables like sum as local variables, simplifying state.

One kind of helpful abstraction is the usage of conditions, in a more immediate form:

private boolean listSizeIsEven() {
    return list.size() % 2 == 0;
}

private boolean nextElementIsThere() {
    return index + 1 < list.size();
}
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • thanx.but only way of handling index in this approach is passing them as arguments,isnt it? – OldSchool Jun 22 '14 at 09:08
  • i am a beginner so do i stick with this methodology or try something of professional type? – OldSchool Jun 22 '14 at 09:13
  • Yes, as argument. There is/was an academic progrmmming language ELAN which followed your design in courses: `index` would then be in global scope (say java field). I thought your methodology given by a good (!) programming course. – Joop Eggen Jun 23 '14 at 06:27
  • About methodology: `listSizeIsEven` seems not needed, when `list` is exposed as variable. On the other hand `nextElementIsThere` could help in explaining the algorithm. I have often seen code where that `+ 1` - falling out of the sky - needs an extra eye check on the code. Code often misses comments too, on what is done for what reason. – Joop Eggen Jun 23 '14 at 06:36
1

There's no point in declaring index at Class level since you dont want it to be a member or an instance of that class. Instead make it local to the method and pass it to other methods as argument where you want to access it.

1

I think you are asking the wrong question.

Your class variables make very little sense, as do many of the methods. This is mostly because:

  1. Your class is doing too much
  2. Your algorithm is a little odd

The class variables that you do have make much more sense passed as method parameters. Some methods need to see them, and some don't.

Your class is also a little odd, in that calling subList twice on the same class will not produce the same answer.

The code is littered with methods I don't quite see the point in, such as:

private boolean noofElementsIsNotOneInList(){
    boolean isnotone = true;
    int size = list.size();
    if ( size == 1){
        isnotone = false;
    }
    return isnotone;
}

Shouldn't this be:

private boolean noofElementsIsNotOneInList(){
    return list.size() == 1;
}

And it makes no sense for it to use some arbitrary List, pass one in so that you know which List you are checking:

private boolean noofElementsIsNotOneInList(final Collection<?> toCheck){
    return toCheck.size() == 1;
}

The same logic can be applied to almost all of your methods.

This will remove the instance variables and make your code much more readable.

TL;DR: Using lots of short appropriately named methods: good. Having those methods do things that one wouldn't expect: bad. Having lots of redundant code that makes things very hard to read: bad.

In fact, just to prove a point, the whole class (apart from the logic to read from stdin, which shouldn't be there anyway) can transformed into one short, recursive, method that requires no instance variables at all:

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);
}

Now you could break this method apart into several appropriately named shorter methods, and that would make sense. It's sometimes more helpful to start from the other end. Sketch out the logic of your code and what it's trying to do, then find meaningful fragments to split it into. Possibly after adding unit tests to verify behaviour.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • what about displaying each compacted list? – OldSchool Jun 22 '14 at 09:05
  • Add a `System.out.println(compacted)` if you wish. – Boris the Spider Jun 22 '14 at 09:06
  • sorry! i am new to java and not studied about the linked list – OldSchool Jun 22 '14 at 09:09
  • The `LinkedList` is another implementation of `List` - in this instance it is no different from an `ArrayList`. It must be accessed using an `Iterator` and does not implement `RandomAccess`. – Boris the Spider Jun 22 '14 at 09:10
  • does System.out.println(compacted) works if compacted were ArrayList – OldSchool Jun 22 '14 at 09:11
  • @okay..but i am using this top down approach in programming is it good? – OldSchool Jun 22 '14 at 09:56
  • As with most things in life, it's good in moderation. Whilst it's good to split complex code into named methods it's bad to split simple code thereby making it hard to follow. Your use of instance variables makes your code almost unintelligible. – Boris the Spider Jun 22 '14 at 09:58
  • i have managed that instances at local scope and have applied your shorthands now my code looks nice and you have provided the ultimate solution...thanx for that.i want to stick,for some time,with this approach of abstraction...hope this couldn't produce bat habit in coding..isn't it? – OldSchool Jun 22 '14 at 10:02
1

what about doing by Recursion:

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);
    }

Steps:

   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
Jayaram
  • 1,715
  • 18
  • 30
  • If you are going to post a code-only answer that doesn't really answer the question, then at least format your code properly. P.S. you don't need an `if...else` if you have a `return` - see my answer for (what I think is) a more elegant approach. – Boris the Spider Jun 22 '14 at 08:53