The trick here is to update only the sections you need - if you update the whole container the browser will reset and refresh from the point where the most recent update happened. In JSF there are a couple of ways to accomplish what you need. More recent versions of JSF (2.3 in particular) has selectors available such as :parent to allow to easier target specific parts of the page to update. With 2.1 you are slightly more limited.
I made a quick test and came up with a working example of your particular user case. The image below is using Font Awesome and PrimeFaces 3.5;

I slightly tweaked your code and made a complete and working example. The controller and backing bean have been separated into separate classes in this example. First let's take a look at the backing bean:
@Data @Named @ViewScoped
public class MyBean implements Serializable {
@Data @RequiredArgsConstructor
public static class Item {
@NonNull private String name;
private boolean selected;
}
private List<Item> items;
@PostConstruct
private void init() {
items = List.of(new Item("a"), new Item("b"), new Item("c"),
new Item("d"), new Item("e"), new Item("f"), new Item("g"),
new Item("h"), new Item("i"), new Item("j")
);
}
}
This defines our data set and uses Lombok to minimize the amount of boilerplate code (@Data and @RequiredArgsConstructor). I also added a selected flag to keep track of if the checkbox is selected or not. This is then used in the controller bean when selecting and deselecting:
@Data @Named @RequestScoped
public class MyBeanController {
@Inject
private MyBean myBean;
public void select(Item item) {
item.setSelected(true);
}
public void deselect(Item item) {
item.setSelected(false);
}
}
Furthermore, here is the view (XHTML) definition:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Button update in UI repeat example</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.0/css/font-awesome.css" />
</h:head>
<h:body>
<table>
<tbody>
<ui:repeat value="#{myBean.items}" var="item">
<tr style="float: left; padding-right: 10px">
<th>
<h:form>
<h:panelGroup id="content">
<p:commandLink rendered="#{item.selected}"
actionListener="#{myBeanController.deselect(item)}"
update="content" process="@this">
<i class="fa fa-check-square-o" />
</p:commandLink>
<p:commandLink rendered="#{!item.selected}"
actionListener="#{myBeanController.select(item)}"
update="content" process="@this">
<i class="fa fa-square-o" />
</p:commandLink>
</h:panelGroup>
</h:form>
</th>
<td><h:outputText value="#{item.name}"/></td>
</tr>
</ui:repeat>
</tbody>
</table>
</h:body>
</html>
As you can see we wrap the buttons inside a form and define an container content that we reference in the update attributes of the p:commandLink tags. Because we are wrapped inside a form the actual id of the panelGroup actually becomes something like j_idt6:9:j_idt8:content with each content id being unique (because each form has a unique id generated).
This allows us to target and update the data of that row specifically.
Theoretically you could update the form directly without wrapping it inside a content. However, doing so interferes with the life-cycle and handling of the form when wrapped inside a ui:repeat. Adding an additional panelGroup and updating that instead, like in this example, solves the issue.