Malvinka
Malvinka

Reputation: 1379

iterating through collection - removing other element

What do I have to do if I would like to remove not only element my iterator points now, but also another one? Task is to write out pairs that sum up to some value (they are stored in HashSet set). Idea: iterate through the set, find second element that sum to what I need, write them out and remove from set both. What I mean, if my set is: [1,2,3] and int sum = 4, then I iterate through, find element 1st: 1, check 4 - 1 = 3, write out (1,3), and then I would like to remove 1 and 3 from the set so that only {2} left. I cannot do this that way of course:

while (itr.hasNext()) {
  int elem = itr.next();
  int toSum = sum - elem;
  if (set.contains(toSum)) {
    System.out.println("(" + elem + "," + toSum + ")");
  }
  itr.remove();
  set.remove(toSum);
}

because of concurrent exception. I found out that solution:

while (itr.hasNext()) {
  HashSet<Integer> notNeeded = new HashSet<Integer>();
  int elem = itr.next();
  if (notNeeded.contains(elem)) {
    itr.remove();
  }
  else {
    int toSum = sum - elem;
    if (set.contains(toSum)) {
      System.out.println("(" + elem + "," + toSum + ")");
      notNeeded.add(toSum);
    }
    itr.remove();
  }
}

but that does not look elegant and efficient (especially space efficient). Is there any better way to do this?

Upvotes: 0

Views: 52

Answers (2)

Azurlake
Azurlake

Reputation: 632

Well, of course you cannot call Set.remove() from within the Iterator because of the concurrent exception, but you can break the iteration and remove it afterwards.

Integer toSumFound = null;
while (itr.hasNext()) {
    int elem = itr.next();
    int toSum = sum - elem;
    if (set.contains(toSum)) {
      toSumFound = toSum;
      System.out.println("(" + elem + "," + toSum + ")");
      itr.remove();
      break;
    }
}
set.remove(toSumFound); //Check for nulls if needed

Upvotes: 0

Andreas
Andreas

Reputation: 159096

I would suggest a second Set with the used values. You can then decide whether to actually remove them from the original set at the end, or not.

Set<Integer> set = new HashSet<>(Arrays.asList(1,2,3,4,5,6));
int target = 7;

Set<Integer> used = new HashSet<>();
for (Integer value1 : set) {
    if (used.contains(value1))
        continue; // already processed
    Integer value2 = target - value1;
    if (! value2.equals(value1) && set.contains(value2)) {
        used.add(value1);
        used.add(value2);
        System.out.println("(" + value1 + "," + value2 + ")");
    }
}
set.removeAll(used); // optional

By making it nondestructive, you can reuse the Set for other searches:

private static void listPairs(Set<Integer> set, int target) {
    System.out.print(target + ": ");
    Set<Integer> used = new HashSet<>();
    for (Integer value1 : set)
        if (! used.contains(value1)) {
            Integer value2 = target - value1;
            if (! value2.equals(value1) && set.contains(value2)) {
                used.add(value1);
                used.add(value2);
                System.out.print("(" + value1 + "," + value2 + ")");
            }
        }
    System.out.println();
}
public static void main(String[] args) {
    Set<Integer> set = new HashSet<>(Arrays.asList(1,2,3,4,5,6));
    listPairs(set, 7);
    listPairs(set, 6);
    listPairs(set, 5);
}

OUTPUT

7: (1,6)(2,5)(3,4)
6: (1,5)(2,4)
5: (1,4)(2,3)

Upvotes: 1

Related Questions